浅议C++对象模型:多重继承下析构函数和构造函数的调用顺序解析

来源:互联网 发布:手机西西软件盒安卓 编辑:程序博客网 时间:2024/05/17 22:56

浅议C++对象模型:多重继承下析构函数和构造函数的调用顺序解析

构造函数

首先看一个例子:

class Father  {public:    int val1;    Father() {        cout << "Father create" << endl;        val1 = 111;    }    ~Father() {        cout << "Father destory" << endl;    }};class Son : public Father {public:    int val2;    Son(int v1, int v2) {        cout << "Son create" << endl;        val1 = v1;        val2 = v2;    }    ~Son() {        cout << "Son destory" << endl;    }};int main() {    Son s(1, 2);}/*Output:Father createSon createSon destoryFather destory*/

毫无疑问,构造函数从继承链顶端开始构造。但是为什么编译器是这样设计的呢?我总结了以下几点:

  1. 子类可能会调用父类定义的成员,譬如Son就使用了Father的val2和Grandfather的val1,如果子类先于父类构造那么val2,val1岂不成为未定义的了?这个是最重要的原因。
  2. 如果子类构造函数在父类构造函数之前,那么初始化后成员的值可能不是我们指定的值。我们在每个构造函数中加上断点,正确的顺序如下:

    • Father构造函数被调用,val1被设为111.
    • Son构造函数最后被调用,val1被设为1(v1), val2被设为2(v2).

       试想,倘若忽略其它问题,Son构造函数最先被调用,Father构造函数后被调用。那么我们最后得到的结果:
      val1 : 111, val2 : 2
      这显然是出乎预料的。

  3. 如果父类的构造函数在子类构造函数之后调用,v_ptr的指向会发生错误。这个问题和问题2很类似。回顾一下v_ptr的构造过程。

    • Father构造函数被调用,v_ptr指向Father的v_table
    • Son构造函数被调用,v_ptr指向Son的v_table

    这样的调用顺序才能保证v_ptr指向正确的v_table。正因为这样,在构造函数中调用虚函数实际调用的是:这个构造函数所属的类的虚函数表中的函数(有点绕口)。

析构函数

析构函数的析构顺序和构造函数相反:先从子类开始,最后到继承链顶端。 为什么要先析构子函数,我只能找到下面的一个解释:

  • 倘若先析构了父类,那么子类中父类的成员终止了。类成员终止了,但是类对象仍然存在,这个状态是矛盾的。所以我们应该先释放类对象,然后释放类成员。
0 0