有三种情况会使用到copy constructor
- 用一个object初始化另一个object
- 传参(pass by value)
- 返回值
default memberwise initilization & bitwise copy semantics
default memberwise initilization
如果我们没有为一个函数提供explicit copy constructor
那么当我们试图用一个object初始化另一个object的时候
内部就使用default memberwise initilization,然后defalut memberwise initilization又可以分为bitwise copy和default copy constructor
当遇到member class object的时候就会以递归的方式施行default memberwise initilization
1 | class String { |
那么Word会先拷贝_count
然后再对_word
实施memberwise initilization
就如同default constructor一样,一个default copy constructor的产生取决于它是否是trival,只有nontrival的instance会被生成
那么是否是trival就在于class是否展现出所谓的bitwise copy semantics
bitwise copy semantics
首先,我们要明确一个class什么时候不展现出bitwise copy semantics
当class内含一个member object而且内含一个copy constructor时,无论是否被显式声明
当继承自一个含有copy constructor(无论是否被显式声明)的class
当class声明了一个或者多个virtual funciton
当class派生自一个含有virtual base class的继承串联
那么,编译器就会弃用bitwise copy而使用default copy constructor
一般而言,如果只含POD数据成员,一般不会合成一个default copy construcotr
最需要注意的是,default copy constructor并不是深拷贝,因此如果涉及到资源的分配,就需要程序员设计一个explicit copy constructor
详情见link3
那么值得讨论的是,为什么以上四种情况不会展现bitwise copy semantics?
1.重新设定vptr
1 | class A { |
因为a的空间大小和b(b+a)的空间大小不同
因此需要将b中b的部分切掉,保留a的部分
此时,如果a中的vptr仍指向b的vtbl(因为bitwise),那么就会blow up
但是如果是指针或者引用,也就是void func (const A &a)
则不会发生sliced,因为指针或者引用所引发的不是内存大小的改变,而是所指向内存的大小和内容的解释方式的改变
所以可以展现多态性,也就是可以将B传入func(const A &a)
并调用B::get()
1 |
|
因为不能使用bitwise
的手法,所以default memberwise initilization
就合成一个copy constructor
,然后在里面显示设置vtpr
处理Virtual Base Class Subobject
还没弄懂virtual继承的对象模型,先留着
程序转化语义学
1.显式初始化操作
1 | X x0; |
这时候会有一个必要的程序转化
1.先声明,这样子其中的初始化操作会被剔除
2.插入class的copy constructor
1 | X x1; |
2.参数的初始化
1 | void func(X x0); |
另一种实现方式是用拷贝构建的方式把实际参数直接构建在其应该的位置上
在函数返回前,destructor会被调用
3.返回值的初始化
1 | X x = bar(); |
如果是函数指针也会进行转化
1 | X (*pf) (); |
Copy Constructor: 要还是不要
1 | class Point3d { |
如上所示,class里面的三个成员都是存储数值的
那么bitwise copy
既安全又快速
那么我们就没必要设计一个copy constructor
实现copy constructor的最简单的方法如下1
2
3Point3d(const Point3d & rhs) {
memcpy(this, &rhs, sizeof(Point3d));
}
然而不管是使用memcpy
or memset
前提条件都应该是class内不含编译器产生的内部member,如vptr
一旦涉及到vptr的改变,就不要使用
例如以下这个错误使用的例子
1 | class Pointer3d { |
但如果涉及到资源的分配,例如指针获取资源
那么就需要我们设计一个copy constructor
这也就是何时使用深拷贝,何时使用浅拷贝的问题