一,关于类的封装
1,类的成员的作用域只能在内部,外部无法访问
2,内外部可以通过类变量访问public成员
3,类成员的作用域与类成员访问级别没有关系
4,struct 默认的属性是public
5,class默认的属性是private
二,构造函数
1,系统会有默认的构造函数(函数体为空)和拷贝构造函数(进行简单的成员变量值复制)
2,如果显示的定义了构造函数,则默认的构造函数不会默认的被调用
3,构造函数可以手动调用
test a(1);//自动调用
test a=1;//自动调用
test a=test(1);//手动调用
4,拷贝构造函数情况
- test a(b);
- test a=b;
- 函数返回一个对象(不能是引用)
- 实参对象传个形参(不能是引用)
- 初始化序列容器中元素时,比如vector<string> svec(5),string的缺省构造函数和拷贝构造函 都会被调用
- 用列表的方式初始化数组元素时,string a[]={string("hello"),string("world")};会调用string的拷贝构造函数
注意:对象之间的赋值时不会调用拷贝构造函数的,拷贝构造函数时初始化,另一个新对象,而赋值运算符不会产生新对象。
函数返回一个对象是引用时,没有调用构造函数。
5,为什么拷贝构造函数必须是引用
如果拷贝构造函数不是一个引用,是传值操作,而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归调用拷贝构造函数,指针也不行,指针传递也值传递。所以必须是引用。
三,初始化列表和析构函数
1,初始化和赋值的区别
初始化:被初始化的对象正在创建
赋值:被赋值的对象已经存在
2,初始化列表
Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
{
// some other assignment operation
}
v1初始化给m1
初始化的成员变量的顺序与变量声明的顺序有关,与初始化列表的顺序无关。
3,初始化列表早于构造函数内部执行。
4,类的const成员变量是只读变量,分配了内存。因为编译器无法获取const成员变量的初始值,所以不会进入符号表。
5,必须使用初始化列表的几种情况
- 类的成员变量有const修饰
- 类的成员为引用
- 类的成员为没有默认构造函数的类类型(也就是说在类中有其他类对象成员)Test() : c(1), m2(3), m1(2),m1和m2是其他类对象名
- 类存在继承关系,派生类必须在其初始化列表调用基类的构造函数。
6,析构函数是C++中对象销毁时做清理工作的特殊函数
析构函数没有参数,没有返回值,对象销毁是自动调用析构函数
7,构造函数和析构函数的调用顺序
如果类中存在其他类对象时:
- 先用初始化列表初始化其他类对象,从而调用其他类对象的构造函数。初始化顺序与声明顺序一样
- 然后调用自身的构造函数,初始化顺序与声明顺序一样
- 调用析构函数的顺序与调用构造函数的顺序相反。
四,构造函数初始化和explicit
test t1(5);
test t2=5;
test t3=test(5);
主动调用构造函数会生成一个临时对象,但不会调用构造函数
1,在现代编译器,这三种方式都可以初始化成功,且创建对象。
2,C++编译器会尝试各种手段尝试让程序通过编译
方式一:尽力匹配重载函数
方式二:尽力使用函数的默认参数
方式三:尽力尝试调用构造函数进行类型转换
3,对于test t2=5;这个方式的初始化内部有一些细节
- 默认情况下,字面量5的类型为int,因此5无法直接用于初始化Test对象;
- 但是编译器在默认情况下可以自动调用构造函数;
- 于是编译器尝试调用Test(int)生成一个临时对象;
- 之后调用拷贝构造函数Test(const Test&)用临时对象对t2进行初始化。
4,但是c++编译器对其作出了优化
中间过程调用构造函数或者拷贝构造函数的行为并不会发生到我们的程序
也就是说,下面三种方式一共就调用了三次构造函数,且没有调用构造函数
test t1(5);
test t2=5;
test t3=test(5);
5,explicit
C++提供了explicit关键字用于阻止编译器对构造函数的调用尝试
test t2=5;会阻止它,编译会报错。