1.理论部分
1.1前言
一般情况下,定义一个class的时候,理论上,编译器可能这个类合成很多东西,有default constructor ,copy constructor ,copy assignment operator,分别是默认构造函数,拷贝构造函数,拷贝指定运算符
1.2不调用构造函数
至于实际上会不会真正的合成,这要看需不需要,需要,这个词很多次出现在《深度探索C++对象模型》中,本人认为,需要的意思就是在没有这些构造函数的情况下,对类的初始化的完全没问题的时候,那就不用合成,比如说 bitwise copy (逐个成员的按位复制)这种方法,而且种方法如果可以的话,它的效率可以说是最高的,但是很多情况下,这种简单的按位复制并不可以完成类的初始化,这个时候,编译器就要使出新武器了,为类合成,下面就是bitwise 失效的四种情况:就以合成default constructor这种情况来说明:
1.3 bitwise copy 失效
1.31第一种
如果A这个类有一个成员变量b,而b是B类的对象,B类有默认构造函数,那么编译器就要为A合成一个默认的构造函数default constructor,可以说是B类的默认构造函数促使编译器帮A合成一个default constructor,在默认构造函数中来调用B的默认构造函数,
1.32第二种
如果A是一个派生类,父类是D,而D是有默认构造函数的,编译器也会合成一个默认构造函数给A,可以说,是父类的默认构造函数促使编译器给A合成的,因为父类本来就有默认构造函数了,子类必须要有default constructor 来调用父类的default constructor 啊
1.33第三种
如果A类中有虚函数,为了实现多态,c++引入了虚函数,由于虚函数是可以有多个版本的,在执行期,才会决定选择哪一个版本以实现多态,根据C++对象模型,编译器会合成一张虚表,表中存放多个虚函数的地址,还要生成一个指针vptr放在对象中,指向虚表,这些指针的指向的操作必须要放在一个默认构造函数,才能完成初始化,编译器要为vprt直接初始化,不行,不像int 那样,直接给个0就完事儿了。
1.34第四种
如果出现虚基类,如果A同时继承与B 和C(多继承),B,C都虚继承于D,根据C++的规定,虚继承的D在内存中只能存有一份对象,由BC共享(为了防止成员的二义性),那么这个指针的指向就比较复杂了,所以必须要合成default constructor,在这里边完成初始化,插入一些支持虚基类,机制的代码
以上四种情况是 合成 default constructor ,copy constructor, copy assignment constructor,的公共前提
2.实际的使用
2.1 应用的时候,产生对象的情况
什么情况下会产生类的对象从而可能调用(default constructor ,copy constructor, copy assignment constructor这些函数的其中一种呢)呢?有三种情况:
2.11,显式初始化的时候
如A a; A a = new A();
2.12:函数传参的时候
是形参,如a(A a);这个时候,会产生一个类的对象,来接受传进来的参数,但是,如果参数是指针或者引用类型的话,不会产生新对象,
2.13:函数有返回值的时候 如:
A a()
{
A a ;
/后面N多操作~~~
return ;
}
A b = a();后面这句 A b = a(); 首先要产生一个对象比如说 A c 来接受a()这个函数的返回值,再把c赋给a;当然,如果返回是指针或者引用的话,不会有新对象出现(虽然如此,但是编译器还是会进行优化的,叫NRV 优化,插入一个应用接受返回值,以达到减少constructor 的调用和 destructor的调用)
以上三种情况均可产生一个对象
2.21 第一个,默认的构造函数
如果我是这样写的,A a;如果我的A这个类本来就没有这个默认的构造函数而且我的A类里边没有指针,也没有虚函数,只有一些基本的类型的成员,如int,float,等类型,那么编译器会自动帮这些成员初始化,一般int这些类型会设为0,但是很多情况下,类里边都会有指针啊,虚函数啊,还有其他类的成员作为成员变量,那么编译器想要给你直接初始化?想太多,因为类的对象不是随随便便就可以帮你指定一个值的,指针也不能帮你正确的初始化,所以
说完默认构造函数,下面看看另外一种对象的初始化调用copy,
2.22第二个:拷贝构造函数
你的代码是A a = A(b); 或者,A a = b; 如果是这种写法,那么编译器的首要操作是把b的值按照成员一个一个的初始化,从b中拷贝到a中,这个叫 bitwise copy按位拷贝,把b中的每一个成员都相应的拷贝给a,如果b中的成员是另一个类的对象,那么拷贝的这个成员时候 ,会递归的拷贝这个成员的内部的参数,但是,这种按位的拷贝,并不是畅通无阻的,和上边的那种编译器默认初始化一样,在一些情况下,会无用武之地,这个时候,编译器会合成以外一种构造函数,copy constructor 拷贝构造函数,那么什么情况下,按位拷贝会失效,默认的构造函数横空出世呢 ,其实个人觉得挺像默认构造函数(default constructor)的,也有四种情况:
(这边和default constructor差不多)
default constructor 和copy constructor 有点像,里边的做的工作也是有一样的地方,比如说虚函数表的初始化,
如果代码是A a ,或者 A a = new A();在上述四种条件下,编译器会合成default constructor
如果代码是A a(b),或者A a= new A(b),或者A a = b,编译器就会合成copy constructor
2.23第三个拷贝指定运算符
copy assignment operator 拷贝指定运算符 这个和a=b,有点不一样,A a=b,是因为a还没有值,用对象b来初始化,会出现合成copy constructor,如果 a已经有值了 就是a = b;
情况差不多也是那四种情况下,bitwise copy失效,那么会调用 copy assignment operator
3.总结:
对象初始化的时候如果class的内容非常简单只有一些简单类型变量,那么无论怎使用一般都是bitwise copy即可完成赋值,如果出现上面说的四种情况:1.基类有相应构造函数,2.成员变量是某个类的对象,该类有相应构造函数。3.类中含有虚函数 4.继承体系中有虚基类,这些情况,bitwise copy 会失效,default constructor ,copy constructor,copy assignment operator,都会被合成,在不同使用情况下,被调用,
A a;就调用default constructor,
A a = A(b),调copy constructor
A a = b; 调用 copy assignment operator
PS:以上的是个人看书《深度探索C++对象模型》时的感悟,不保证全是正确的,申明:我还是个菜鸟,如果有大神看到了,发现错误,欢迎指正,请轻拍