[原]C++:子类的构造、拷贝和析构
本文仅考虑单继承的情况。——多继承复杂性比较高,暂不讨论。
考虑
class AA
{
// ...
private:
TYPE m_1;
};
class BB: public AA
{
// ...
};
这种情况下,~AA()必须是virtual,原因不必多说——这是使用C++来作OO开发的best practice。这就引入了一个问题:虚表的处理。
首先观察一下C++的对象模型。对于含有虚函数的类来说,其成分有数据成员、成员函数、虚表、静态成员。而该类的一个实例仅仅包含数据成员和指向虚表的指针。对于成员函数和静态成员来说,所有的实例共享一个拷贝——成员函数是代码共享,而静态成员则是存储共享。
构造
对于AA,构造很简单:获取一块内存(栈/堆),初始化成员。注意ctor里有个误区:写在ctor中的“=”并非是初始化,而是拷贝/赋值。这也是需要初始化列表的原因。成员的构造函数有参数将更明显:
AA(TYPE _aTYPE): m_1(_aTYPE) {}
如果写成
AA(TYPE _aTYPE){ m_1 =_aTYPE; }
很明显,当TYPE提供了带参数的ctor时,第二种形式的构造就是错的。
如:
编译时将会有错误:
candidates are: AA::AA(int)
说了点题外话。言归正传。
当构造子类BB的对象时,首先构造属于AA的部分。此时,调用AA的构造函数,并使vptr指向AA的虚表。这时存储区的类型是AA。然后,开始构造属于BB的部分,调用BB(),并使vptr指向BB的虚表。相当于:
拷贝和赋值
有了前面的讨论,下面的内容就好理解了。
为了保证正确拷贝父类部分,拷贝构造和赋值操作符都需要调用父类的相应函数:
析构
与构造相反。析构BB的实例时,先调用~BB(),将BB的部分析构掉,并使vptr指向AA的虚表。此时对象的类型为AA。然后才是调用~AA()。
关于构造,关键点在于BB(/*...*/){//...}中,“)”与“{”之间编译器插入了很多代码,这些代码在幕后做了很多工作;而关于析构,关键点在于~BB(){/*...*/},最后一条语句与“}”之间,编译器插入了很多作幕后工作的代码;对于拷贝和赋值,就需要显式加上对父类的拷贝和赋值的调用——因为默认的拷贝/赋值行为是按位进行的。
参考
Inside the C++ Object Model, by Stanly B Lippman
Effective C++, by Scott Meyers
The C++ programming Language, by Bjarne Stroustup
Copyleft (C) 2007-2009 raof01.
请保留此权利声明,并标明文章原始地址和作者信息
考虑
class AA
{
// ...
private:
TYPE m_1;
};
class BB: public AA
{
// ...
};
这种情况下,~AA()必须是virtual,原因不必多说——这是使用C++来作OO开发的best practice。这就引入了一个问题:虚表的处理。
首先观察一下C++的对象模型。对于含有虚函数的类来说,其成分有数据成员、成员函数、虚表、静态成员。而该类的一个实例仅仅包含数据成员和指向虚表的指针。对于成员函数和静态成员来说,所有的实例共享一个拷贝——成员函数是代码共享,而静态成员则是存储共享。
构造
对于AA,构造很简单:获取一块内存(栈/堆),初始化成员。注意ctor里有个误区:写在ctor中的“=”并非是初始化,而是拷贝/赋值。这也是需要初始化列表的原因。成员的构造函数有参数将更明显:
AA(TYPE _aTYPE): m_1(_aTYPE) {}
如果写成
AA(TYPE _aTYPE){ m_1 =_aTYPE; }
很明显,当TYPE提供了带参数的ctor时,第二种形式的构造就是错的。
如:
|
no matching function for call to ‘AA::AA()’
candidates are: AA::AA(int)
说了点题外话。言归正传。
|
|
|
拷贝和赋值
有了前面的讨论,下面的内容就好理解了。
为了保证正确拷贝父类部分,拷贝构造和赋值操作符都需要调用父类的相应函数:
|
析构
与构造相反。析构BB的实例时,先调用~BB(),将BB的部分析构掉,并使vptr指向AA的虚表。此时对象的类型为AA。然后才是调用~AA()。
关于构造,关键点在于BB(/*...*/){//...}中,“)”与“{”之间编译器插入了很多代码,这些代码在幕后做了很多工作;而关于析构,关键点在于~BB(){/*...*/},最后一条语句与“}”之间,编译器插入了很多作幕后工作的代码;对于拷贝和赋值,就需要显式加上对父类的拷贝和赋值的调用——因为默认的拷贝/赋值行为是按位进行的。
参考
Inside the C++ Object Model, by Stanly B Lippman
Effective C++, by Scott Meyers
The C++ programming Language, by Bjarne Stroustup
Copyleft (C) 2007-2009 raof01.
请保留此权利声明,并标明文章原始地址和作者信息