深入探索C++对象模型(三)拷贝构造函数

拷贝构造函数的。

1. 有三种情况会以一个对象的内容作为另一个类对象的初始值:

a. 明确的以一个对象初始化另一个对象,例如: A a1 = a2;//会调用类A的拷贝构造函数初始化a1。

b. 对象被作为参数传递给某个函数时:foo(A a){...}; A a1; foo(a1);//a1作为参数的初值。

c. 当传回一个类对象时,A foo() {A a1; return a1;}。

 2. 编译器是否会合成拷贝构造函数,取决于程序是否展现出按位拷贝语义(bitwise copy semantic),如果展现出该语义那么,编译器并不会合成拷贝构造函数,而是按成员初始化(member wise initialization),即递归的将所有数据成员依次赋值。书中的例子如下:


如果程序未展现该语义,那么编译器会合成拷贝构造函数,如下四种情况会合成拷贝构造函数(即展现非bit wise copy semantic):

a. 类中包含含有拷贝构造函数(不论该拷贝构造是明确声明的还是编译器合成的)的数据成员。

b. 当类继承自含有拷贝构造函数(不论是明确声明的还是编译器合成的)的基类。

这两种情况系统会将该数据成员或者基类的拷贝构造函数安插到合成的拷贝构造函数中。

c. 当类声明了多个虚函数。编译器必须为通过拷贝构造的对象正确的初始化虚表指针,这种情况发生于当子类对象拷贝到父类对象中时,例如,当子类对象yogi拷贝到父类对象franny(父类是值对象,而不是指针和引用,后两种情况会产生多态行为)中时,必须为franny初始化合适的虚表指针,而不能通过按成员初始化(member wise initialization),所以必须合成一个构造函数,安插相关代码到其中执行这个修改


d. 当类派生自一个继承串链,其中有一个或者多个虚基类时。与上一种情况类似,编译器必须保证子类中虚基类子对象位置的确定性,而按位拷贝可能会破坏这个位置信息,尤其是当子类对象拷贝到父类对象中时,编译器必须合成一个拷贝构造函数进行正确的处理,例如,此种情况下,编译器必须合成一个拷贝构造函数,安插一些代码,设定虚基类指针或者偏移量,对每一个成员执行必要的member wise initialization以及其他内存相关工作(第三章虚基类部分会由详细介绍)


总的说起来,与合成默认构造函数类似,合成拷贝构造函数只有在编译器需要的情况下才会发生。例如编译器需要调用某些拷贝构造函数,这些调用代码必须被安插到本类的拷贝构造函数中,如果本类没有拷贝构造,则必须要为其合成一个,又或者需要在拷贝构造函数中进行一些指针的初始化操作。


1. 对于明确的初始化操作:

拷贝构造函数会被调用,以上代码会被转化成:


2. 关于参数的初始化,参数按值传递到函数中会有临时对象产生:


以上并没有结束,在调用foo( __temp0 )时,参数的值传递会被改为引用传递void foo(X& x0),否则又会产生临时对象,这样就无法终止了。

3. 关于返回值的初始化,左侧的代码会转化为右边的代码。


同时,对于所有函数调用也会进行相应的转化:X xx = bar(); 会转化为X xx; bar(xx);对于函数指针也会有相应的转化:X (*pf) ();转化为void (*pf) (X& )。

4. 对于下面三个初始化语句的实际转化之后的语句见右侧,注意第一句和后面二三句是不一样的。


5. 对于数据成员的初始化操作,有四种情况需要使用初始化列表(member initialization list):


某些情况虽然可以不用初始化列表,但是会导致效率不佳,会有临时对象的开销:


若使用初始化列表,会更有效率,省去了临时对象的开销:


初始化列表到底发生了什么:编译器会一一操作初始化列表,以适当的次序在构造函数中安插初始化操作,并且在任何构造函数已有的代码之前,例如上图中的例子。

关于初始化列表的一个陷阱就是初始化列表并不是按照列表的顺序进行初始化的,而是根据数据成员在类中的声明顺序来定的,例如:


上述代码原意是要使用val初始化j,然后使用j初始化i,但实际情况并不是这样,因为在了类X中,i的声明先与j,所以初始化列表会先使用j初始化i,而此时j并未初始化,所以i得到的是无意义的值。改成如下,就没有问题了。

但是这里有一个比较有趣的问题,按照声明的顺序i先被初始化,还是有为题啊,其实不然,注意上面的描述,实在初始化列表中,初始化按照声明顺序,还有一点就是初始化的代码被安插到构造函数中时,一定会按照到已有代码的前面,并不会有问题:

[cpp]  view plain  copy
  1. X::X(int val)  
  2. {  
  3.     j = val;  
  4.     i = j;  
  5. }  

猜你喜欢

转载自blog.csdn.net/coolwriter/article/details/80551107