以一个object的内容作为另一个class object的初值的三种情况:
- 对一个object做显式的初始化操作; class X{};X x; X xx = x;
- 当object被当做参数交给某个函数 extern void foo(X x); void bar() { X xx; foo(xx);…}
- 当函数传回一个class object时 X foo_bar() {X xx; …return xx;}
如果class没有提供一个explicit copy constructor又当如何?当class object以“相同class的另一个object”作为初值,其内部是以所谓的default memberwise initialization手法完成。
Default Memberwise Initiazation: 把每一个内建的或派生的data member(例如一个指针或一个数组)的值,从某个object拷贝一份到另一个object身上。不过它并不会拷贝其中的member class object,而是以递归的方式施行memberwise initialization。
什么时候一个class不展现出“bitwise copy semantics”呢?(需要编译器合成)
有4种情况:
- 当class内含一个member object而后者的class声明一个copy constructor时(不论是被class设计者显示地声明,就像前面的String那样;或是被编译器合成,像class Word那样)。
- 当class继承自一个base class而后者存在一个copy constructor时(再次强调,不论是被显式声明或是被合成而得);
- 当class声明了一个或多个virtual functions时;
- 当class派生自一个继承串链,其中有一个或多个virtual base classes时。
重新设定Virtual Table的指针(第三种情况)
当class声明了一个或多个virtual functions时,编译期间的两个程序扩张操作:
a. 增加一个virtual function table(vtbl),内含每一个有作用的virtual function的地址;
b. 一个指向virtual function table的指针(vptr),安插在每一个class object内。
当编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics了。现在,编译器需要合成出一个copy constructor以求将vptr适当地初始化,下面是个例子:
class ZooAnimal {
public:
ZooAnimal();
virtual ~ZooAnimal();
virtual void animate();
virtual void draw();
//...
private:
// ZooAnimal的 animate()和draw()所需要的数据
};
class Bear : public ZooAnimal{
public:
Bear();
void animate();
void draw();
virtual void dance();
private:
// Bear 的 animate()、draw()和dance()所需要的数据
};
Bear yogi; // default Bear constructor初始化, yogi的vptr被设定指向Bear class的virtual table (靠编译器安插的代码完成)
Bear winnie = yogi; // 把yogi的vptr值拷贝给Winnie的vptr是安全的
ZooAnimal class object以另一个ZooAnimal class object作为初值,或Bear class object以另一个Bear class object作为初值,都可以直接靠“bitwise copy semantics”完成(除了可能会有pointer member之外。为了简化,这种情况被我革除)。
当一个base class object以其derived class的object内容做初始化操作时,其vptr复制操作也必须保证安全,例如:
ZooAnimal franny = yogi; //这会发生切割sliced 行为
franny的vptr不可以被设定指向Bear class 的virtual table(但如果yogi的vptr被直接“bitwise copy”的话,就会导致此结果),否则当下面程序片段中的draw()被调用而franny被传进去时,就会“炸毁”:
void draw(const ZooAnimal& zoey)
{
zoey.draw();
}
void foo()
{
// franny的vptr指向ZooAnimal的virtual table,而非Bear的virtual table(它由yogi的vptr指出)
ZooAnimal franny = yogi;
draw(yogi); // 调用Bear::draw()
draw(franny); // 调用ZooAnimal::draw()
}
处理Virtual Base Class Suboject(第四种情况)
Virtual Base Class 的存在需要特别处理。一个class object如果以另一个object作为初值,而后者有一个virtual base class suboject,那么也会使“bitwise copy semantics”失效。
每一个编译器对于虚拟继承的支持承诺,都代表必须让“derived class object 中的virtual base class suboject位置”在执行期准备妥当。维护“位置的完整性”是编译器的责任。“Bitwise copy semantics”可能会破坏这个位置,所以编译器必须在它自己合成出来的copy constructor中做出仲裁。