构造函数语义学——Copy Constructor的建构操作
Posted wangziqiang123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了构造函数语义学——Copy Constructor的建构操作相关的知识,希望对你有一定的参考价值。
?
在三种情况下,会以一个object的内容作为另一个class object的初值:
object明确初始化
1
2
3class ...;
X x;
X xx = x;object被当作参数交与某个函数
1
2
3
4
5extern void foo(X x);
void bar()
X xx;
foo(xx);函数返回值是一个class object
1
2
3
4
5X foo_bar()
X xx;
...
return xx;
如果开发者已经明确定义了一个copy constructor如下:
1 | //copy constructor可以是多参数,其中有一个参数是其class type |
那么在大部分情况下,当class object以另一个同类实体作为初值时,上述constructor会被调用,这可能会导致一个暂时性class object的产生或程序代码发生改变(或二者都有)。
Default Memberwise Initialization
?
如果函数并没有提供一个explicit copy constructor,那么其拷贝同类型对象的操作由default memberwise initialization完成,其执行策略为:对每一个内建或派生的data member的值,从某一个object拷贝到另一个object。不过它不会拷贝其中的member class object,而是实施递归式的memberwise initialization(对每一个对象依次执行default memberwise initialization)。
实例
考虑如下class声明:
1 | class String |
String object的default memberwise initialization发生于这种情况:
1 | String noun("book"); |
其完成方式类似于依次各别设定每一个member:
1 | verb.str = noun.str; |
如果String是另一个class的member:
1 | class Word |
那么一个Word object的default memberwise initialization会拷贝其内建的member _occurs,然后对String member执行递归式memberwise initialization。
如果开发者没有为class指定copy constructor,那么编译器也会生成implicit的声明与定义。类似于Default constructor,C++亦把copy constructor区分为trivial与non-trivial两种,只有non-trivial的实体才会被合成于程序之中。决定一个copy constructor是否为trivial的依据是class是否表现出所谓的“bitwise copy semantics”。
Bitwise Copy Semantics
1 |
|
很明显verb是根据noun来初始化,但在未了解Word class声明之前,我们无法预测该初始化操作的程序行为。如果开发者定义了copy constructor,那么编译器会调用它。如果没有,编译器会根据class 是否展现出bitwise copy semantics,来合成一个copy constructor。
举例而言,如果Word内部数据仅仅含有内置类型,那么编译器不会合成copy constructor,而是执行bitwise copy。但如果Word内含一个String object,而String class存在一个explicit copy constructor,那么编译器将不得不合成一个copy constructor,以调用member object的copy constructor,在该合成的copy constructor中,内置类型仍然使用bitwise copy。
Bitwise Copy不出现的情况
?
一个class有四种情况不会表现出bitwise copy:
- class内含一个member object,而后者有一个copy constructor(无论是开发者指定还是编译器合成)
- class继承自一个base class,而后者存在一个copy constructor(无论是开发者指定还是编译器合成)
- class声明了一个或多个virtual functions
- class存在的继承体系内存在一个或多个virtual base class
在前两种情况下,编译器会将member或base class的copy constructors插入至合成的copy constructors中。
重设定vptr
?
在上一节我们曾经阐述过,含有virtual functions的class会在编译期间构造函数会自发扩张:
- class会增加一个vtbl
- class object会增加一个vptr
显然,vptr是决定多态机制能否正确运行的关键,当编译器将vptr导入至class之中时,该class不再具备bitwise semantics。编译器需要合成一个copy constructor,将vptr合理初始化。
实例
现有继承体系及class声明如下:
1 | class ZooAnimal |
ZooAnimal object相互赋值或者Bear object相互赋值都可以通过bitwise copy semantics完成。在这种情况下,vptr保持bitwise copy是安全的。
当一个base class object以其derived class object内容作初始化操作时,其vptr也需要保证安全:
1 | Bear B; |
显然,Z的vptr不应该指向Bear的vtbl,也就是说,Base class被合成出来的copy constructor会明确设定object的vptr指向Base Class的vtbl,而非从rhs处执行bitwise copy。
处理Virtual Base Class Subobject
?
一个class object如果以另一个object作为初值,而后者带有一个virtual base class subobject,那么bitwise semantics同样会失效。
在上一节中我们已经编译器需要保证在运行期明确virtual base class subobject的位置。Bitwise copy可能会破坏这个位置。
实例
现有继承体系及class声明如下:
···C++
class Raccoon:public virtual ZooAnimal
public:
Raccoon();
Raccoon(int val);
…
private:
…//some data
1 | 在开发者撰写的constructor中,编译器会生成: |
现有object分析图如下:
在下述代码中,编译器无法判断bitwise copy是否有效,因为编译器无法了解Raccoon是否指向了一个真正的Raccoon对象:
1 | Raccoon *ptr; |
总结
?
我们是否可以认为,在bitwise copy完全合理的情况下,应当禁止调用copy constructor以优化程序?这个问题将会在后续章节中讨论。
本节我们讨论了class不再保持bitewise copy semantics的四种情况,在这四种情况下,如果未声明copy constructor,那么编译器为了保证初始化正确,将会合成一个copy constructor。
原文:大专栏 构造函数语义学——Copy Constructor的建构操作
以上是关于构造函数语义学——Copy Constructor的建构操作的主要内容,如果未能解决你的问题,请参考以下文章