c++是基于c的面向对象程序设计语言,它的开发效率非常高,因此,对于c++的学习,是很必要的。同时,笔者是一个android开发人员,了解c++对于了解android的底层机制是特别有用的。而如果是嵌入式工程师或者系统开发工程师,就更加需要了。所以各方面考虑,无论作为哪方面的开发者,学习c++都是只有好处没有坏处的。下面我对c++基本知识进行一个概要的总结,帮助大家了解整体基础机构。此文章只适合有c++基础(或者有经验的开发人员)的人看。
1、C++主函数
int main()
{
return 0;
}
每个C++程序都含有一个且只有一个main函数作为程序的入口,每个C++函数的定义特点都是又返回值 函数名 形参列表 以及花括号组成。在main函数中,返回0表示程序成功执行,返回其他值表示执行过程发生错误。
2、C++文件命名规范
每个c++文件都以后缀,cpp或者.c结尾。比如file.cpp或者file.c都是合法的c++文件。
3、输入与输出对象
c++提供了两个标准库对象,分别为istream和ostream。前者表示输入流对象,后者表示输出流对象。其中cin、cout、cerr、clog是最常用的。cin用于读取数据,cout用于输出数据,cerr用于打印错误信息,clog用于打印一般信息。下面给出一个使用了基本输入输出语句的例子:
#include<iostream>
int main()
{
int a = 0, b = 0;
std::cout << "请输入两个数,用于求差:" << std::endl;
std::cin >> a >> b;
std::cout << "a-b的值是:" << a - b << std::endl;
return 0;
}
如上所示:其中#include<iostream>表示将系统库的文件包含到当前的文件里面,这样,就可以使用里面定义的函数了(cin.....来自那里)。std::cout表示输出字符,std是命名空间的意思,::是作用域操作符,表示右边的操作对象来自左边的操作对象。std::cout的意思是cout来自于std。std::endl的作用在于刷新缓冲区,会将数据数据立即显示出来,此对象会含有换行符,因此输出的内容会自动换行。
4、注释
和很多高级程序设计语言一样c++也含有多行注释和单行注释两种。看例子:
int main()
{
/*
多行注释
执行相减操作
*/
int a = 0, b = 0;
//单行注释
std::cout << "请输入两个数,用于求差:" << std::endl;
std::cin >> a >> b;
std::cout << "a-b的值是:" << a - b << std::endl;
return 0;
}
注意:注释是不可以嵌套使用的。
5、C++中的类
c++是面向对象的程序设计语言,因此也可以自定义类。但是它的类有两种定义方法(class和strcut),每一个类都会包含成员变量和成员函数,以及访问修饰符下面看一个例子:
#include<string>
class Person
{
public:
Person();
Person( std::string nameParam)
{
name = nameParam;
}
~Person();
std::string getInfo()
{
return name;
}
private:
std::string name;
};
Person::Person()
{
}
Person::~Person()
{
}
#include<iostream>
#include "Person.h"
int main()
{
/*
多行注释
执行相减操作
*/
int a = 0, b = 0;
//单行注释
std::cout << "请输入两个数,用于求差:" << std::endl;
std::cin >> a >> b;
std::cout << "a-b的值是:" << a - b << std::endl;
Person person("huangyi");
std::cout << person.getInfo() << std::endl;
return 0;
}
6、基本类型
C++是静态类型语言,因此在编译的时候会检查变量的类型,所以必须要正确了解基本类型。基本类型如下:
上述基本类型中,bool,char,wchar_t,short,int ,long都是整型,其中bool在c++中是可以用算术常量来赋值的,0表示false,其余的任何值都表示true。char,wchar_t都是字符类型,前者是基本的字符集对应的值,后者是拓展字符集(比如中文日语)。int,short,long都是整型,都可以分为有符号和和无符号两种(sighed int和unsighedint)。整型都是有取值范围的,一般来说如果超过了取值范围的值,就会取该值对256求模后的值(超出范围的处理会根据编译器的不同而采用不同的策略)。double,float,longdouble都是浮点型数据,即带小数点的数值。double64位,float32位,long double94位。
7、字面值常量
整型的字面值常量可以有三种表现形式,分别是10进制、2进制、16进制。
比如:18=022=0xf2。
根据后缀可以分为无符号型和长整型:10u,10L,建议用大写的L代替小写的l,这样容易区分1和L。
浮点型默认是double型,因此如果表示float的话,比需添加f后缀:10.02f。
8、变量
变量分为左值和右值,左值可以放在操作符的左边和右边,右值只能放在右边。变量命名应该遵循驼峰命名法,其次不可以使用c++关键字和保留字作为变量名称:
保留字:
9、声明和定义
c++中一个程序中一个名字只能被定义一次,但可以声明多次。声明和定义的区别在于,定义会根据变量类型来分配存储空间,声明则不和分配。声明通常是通过extern来声明的。考虑下面三种情况:
int a=0;
extern int b=1;
extern int a;
第一种是定义,会分配空间。第二种虽然使用了extern,但是他却初始化了,因此也是一种定义。第三种就是声明。
10、const限定符
const的作用在于限定一个变量为常量,即变量定义之后就不可以修改。再使用const限定符的时候,该值默认的为文件内可以访问。即即使你将一个const变量定义为全局变量,别的文件也不能访问他,除非你加上了extern修饰符,这样就可以访问了。(一般的变量默认是有extern修饰符的,因此文件外可以访问,但是const默认是没有extern限定符,因此为见外不可访问)。
11、引用
引用是一种符合类型,特可以表示变量的别名,这样在操作引用对象的时候,就和操作变量一样。引用对象一旦初始化了,就不可以更改所引用的对象。另外,引用不可以引用引用对象。看下边的例子:
//引用
int c = 10;
int &refC = c;
refC++;
std::cout <<c << std::endl;
12、枚举类型和类类型(struct)
枚举以关键字enum和名称来定义。如下:
//枚举
enum type
{
one,two,three
};
如果枚举里面的成员没有初始化的话,默认是从0开始递增的,比如这里是one=0,two=1......
struct也是用于定义一个类类型,他和class的不同在于,struct的默认成员的访问修饰符是public,而class是private。即在我们用struct定义一个类类型的时候,在遇到第一份访问修饰符之前定义的成员都默认是public的。
13、包含文件<>和”“的区别
<>一般用来包含系统标准库的头文件,”“一般是包含自定义的头文件。前者搜索文件的时候从当前编译器设置的路径环境变量里查找的,后者则是从当前项目的根目录开始查找。
14、using声明
using生命可用于声明某个对象属于哪个空间,这样使用它的时候就不需要再次使用命名空间的前缀了。同时一个using只能声明一个对象,多个对象需要声明必须重复使用using。比如:在main函数外声明:
using std::cerr;
使用cerr的时候就不用使用std::前缀来声明使用空间了。
//cerr由于用了using声明,所以可以直接使用
cerr << "出错了" << std::endl;
但是要注意的是,在头文件中,不要使用using声明,因为头文件是在预编译的时候赋值带包含该头文件 的文件中的,因此如果使用了using声明,导致每一个包含它的文件都含有using声明。这样可能会导致重复声明。必须养成一个好的习惯就是,头文件必须只定义必要的东西。
15、string类型标准库
String是C++提供的一个用于操作长度可变的字符串的标准库,它提供了字符串相关的基本操作,并且它的内存管理由C++标准库负责,所以是个很好的字符串使用标准库。注意由于兼容C语言的原因,C++的string标准库和字符串字面值使用的时候不一样的。使用它需要声明使用空间,如下:
#include <string>
15.1 string类型的数据初始化方法有如下几个重载
注意在C++中如果定义一个变量而没有初始化的话,会使用默认构造函数进行初始化。
15.2 string类型数据的输入输出
string类型数据的输入输出可以使用标准的iostream库读取,但是读取字符串值得时候,有两个点需要注意:
①、字符串开头的空白字符(换行符、回车符、制表符)会被忽略直到遇到第一个非空白字符位置。
②、字符串从第一个字符开始直到遇到第一个空白字符为止,即使空白字符还有字符也不会被读取。
看下面的例子:
#include <string>
#include <iostream>
using std::cin;
using std::endl;
using std::cout;
using std::string;
int main()
{
string s;
cout << "输入一个字符串:";
cin >> s;
cout << s << endl;
system("pause");
return 0;
}
我们输入含有空格的字符串,看看输出的结果:
15.2.3 getline
对于string类型,C++还提供了一个getline函数,此函数接受两个参数,一个是istream,一个是string对象。此函数每次都返回istream对象。因此可用于循环读取文本信息。此函数的特点是,遇到换行符才会结束输入,这和cin读取内容的凡是不同。看下面的代码:
/*while (cin >> s)
{
cout << s << endl;
}*/
while (std::getline(cin, s))
{
cout << s << endl;
}
运行效果图:
此函数只有遇到换行符才会结束输入,因此可以带空格。
15.3 string的基本操作
注意size函数返回的结果不是整型,而是string::size_type类型,这是一个定义在string标准库中的unsigned类型,它可以用于表示任意长度的字符串长度,它的使用方法如下:
string::size_type size(s.size());
cout << size << endl;
15.4 string字符的处理函数
这些函数同样可以处理char类型数据,它们定义才cctype标准库中,函数表如下:
上面大部分用于判断的函数,如果说判断结果为真,那么将返回非0值,否则的话返回0。
16、vector模板
vector是C++的一个标准库,用于存放一组数据类型相同的元素的集合,它的存储空间由标准库直接管理。使用vector之前,需要先声明,如下:
#include <vector>
需要注意的是vector是一个类模板而不是一个数据类型,为什么这么说呢,因为vector是一个泛型容器,会跟随指定的数据类型不同而不同。比如victor<int>和vector<float>就是两种不同的数据类型。
16.1 初始化
vector的初始化方法如下:
如果要将v1赋值给v2,那么v1和v2必须含有相同的数据类型。如果调用v4的方法,那么初始化的值是根据包含的元素数据类型来进行默认初始化的。比如int全部初始化为0,string则使用默认构造函数。使用vector最高效的方法就是初始化一个空的vector对象,这样在后续操作可以通过动态添加元素来拓展,不同于其它编程原因需要预先申请存储空间。在c++中,使用动态增长的效率反而是最高的。
16.2 vector的基本操作
size返回的结果是vector::size_type类型,一般用于添加元素都是通过调用push_back()函数进行添加的,切记通过下标只能获取已经存在的值,不能对不存在的值进行操作。如下面的操作:
vector<int> group;
for (vector<int>::size_type i = 0; i <= 10; i++)
{
group[i] = i;
}
17、迭代器
迭代器是一种检查容器内元素并便利元素的类型。因为在c++标准库中对大多数容器类型都提供了迭代器的支持,而只有少部分才支持下标操作,因此,迭代器对于遍历容器内容是很重要的。而每一种数据类型都有自己的迭代器,比如vector<int>::iterator。为了起到起始点和重点的作用,每一种容器都提供了begin和end操作,其中begin返指向的是第一个元素,end指向的是最后一个元素的下一个元素,这个可以在遍历的时候起到哨兵的作用。当我们需要获取iterator所指向的数据元素的值的时候就需要使用*(解引用操作符)来获取内容。下面是一个典型的案例:
vector<int> group;
for (vector<int>::size_type i = 0; i < 10; i++)
{
group.push_back(i);
}
vector<int>::iterator groupIterator = group.begin();
for (groupIterator; groupIterator != group.end(); groupIterator++)
{
cout << *groupIterator << " ";
}
cout << endl;
同时每个容器还有一个const_iterator的对象,这个对象返回的操作对象是只可以读取它指向的元素的值但是不可以修改。要注意区分const_iterator和const iterator对象,前者是可以修改自身的值但是不可以修改指向元素的值,后者可以修改指向元素的值,但是自身的值不可以修改。
18、数组
数组是由类型,名称,维数组成,其中类型可以使任意的内置类型,类类型,或者复合类型(引用除外,没有数组的所有元素的是引用的),维数表示的是当前数组的容量。
18.1 初始化规则
数组的维数必须是编译阶段就能确定的大于0的值,比如整型字面值常量(int group[100]),整型const变量(const int size=100,int group[size]),或者常量表达式的const对象(const int size = 100,int group[size+1]).如果定义一个数组没有显示初始化,那么内置类型全部默认为0,类类型会默认构造函数,如果没有提供默认构造函数,必须为给数组的元素显示初始化。
18.1.1 特殊字符的初始化
在初始化字符数组的时候,字符字面值和字符串字面值是不同的。考虑下面三个数组:
char cal[]={'d','a','y'}
char cal1[]={'d','a','y','\0'}
char cal2[]="day"
其中cal的长度是3,而下面两个则是4。因为字符串字面值是含有一个空字符的,即cal1和cal2是等价的。
18.1.2数组的直接初始化和赋值初始化
在C++中数组之间是不可以互相赋值的,考虑下面的两种情况:
int cal[3]={1,2,3}
int cal2[](cal)
int cal3[]=cal;
上面试图将cal数组对象分别赋值给cal2,cal3,但是上面两种都会初始化失败。
18.2数组的下标操作
不同于其他高级程序语言,在C++中,操作是组下标对象的类型是size_t,这是一个可以随着机器的不同而拥有不同长度的类型。记得访问vector的时候,用的是size_type,其实内部的源码是typedef size_t sizeType,即size_type也是size_t类型。使用下标访问数组元素的操作如下:
/*
数组
*/
const size_t size = 10;
//数组的维数必须是const常量,初始化的时候,如果没有显示初始化的元素,内置类型一律为0
int cal[size] = { 1, 2, 3, 4, 5 };
int cal2[size];
//如果要将cal复制给cal2,可以如下做
for (size_t i = 0; i < size;i++)
{
cal2[i] = cal[i];
}
//输出结果
for (size_t i = 0; i < size; i++)
{
cout << cal2[i];
}
cout << endl;
//输出结果
for (int i = 0; i < size; i++)
{
cout << cal2[i];
}
cout << endl;
注意数组不要越界操作。
19、指针
在vector和string中,可使用下标和迭代器来操作元素对象,而在数组中,则可以使用下标和指针来操作元素对象。在指针上,可以使用解引用操作符*来获取指针所指的对象的值,使用++来移动指针。指针的初始化则需要用到取地址操作符&。比如:
//使用指针
string s = "happy";
string *pS = &s;//字符串指针
cout << *pS ;
cout << endl;
char sGroup[] = "happy";
char *pC = sGroup;//数组指针
for (int i = 0; i < 6; i++)
{
cout << *(pC + i);
}
cout << endl;
指针的值可以是其它变量的地址,或者是0,0表示不指向任何对象,也可以是NULL,它在编译的时候会自动替换为0。要避免使用未初始化的指针。
19.1 引用和指针的区别
引用在定义的时候必须初始化,并且一旦定义就不允许修改,即始终指向该对象。任何对引用的操作都是对它指向的对象的操作。而指针如果没有使用解引用操作符,则是修改指针的值,如果使用了解引用操作,则修改的是它指向的对象的值。考虑下面两组代码:
int a=10,b=20;
int *pa=&a,*pb=&b;
pa=pb;
int &relA=a,&relB=b;
relA=relB;
经过赋值操作,将pa指向的对象改成了b,而relA的指向对象仍然是a,但是a的值变成了20。
19.2 指针和const限定符
指针和const之间有很微妙的组合。首先是指针所指向的对象是一个const对象,这意味着不能通过指针修改它所指向的对象。由前面可以知道,如果是一般的指针,是可以修改指针所指对象的值的。所以指向const对象的指针必须也声明为const类型。如下:
const int a=10;
const int *p=&a;
这样,通过指针只能修改指针自身的值而不能修改指针所指变量的值。
如果是要定义一个不能修改所指对象的指针,则这样使用:
int a =10;
int const *p=&a;
这样子声明的指针,不可以再修改自身的值,但是却可以修改指向的对象的值。
如果要定义一个既不能修改自身也不能修改所指变量的值的指针,如下:
const int a=10;
const int *const p=&a;
20、创建动态数组
我们知道数组在创建的时候,需要明确知道维数,并且数组在除了作用域语句块之后就不存在了,这样的限制在实际开发中往往是很难接受的。因此,C++中就有了动态数组这个东西。通过new可以创建一个动态数组,如下:
int *p=new int[10];
new会申请一块可存放10个int类型数据的内存,并返回首元素的地址。这样通过操作p就可以操作数组元素了。如果要初始化动态数组,就这样写:
int *p=new int[10]();
这样就会创建一个动态数组,并且值都是0.如果是类类型,则会用默认构造函数进行初始化。
当创建了动态数组之后,必须要释放,否则会减少可用内存,释放如下:
delete [] p;
他的意思是释放p所指向的数组。注意[]的存在。如果不写[],会导致内存泄漏。