构造函数和析构函数调用

时间:2013-04-22 09:22:02

标签: c# c++ oop

当我们创建派生类的对象时, 为什么构造函数以从上到下的方式调用(首先是基础构造函数,然后是派生构造函数),并且是从底部到顶部的析构函数(第一个派生构造函数,然后是基础构造函数)

3 个答案:

答案 0 :(得分:5)

要解释新手,请考虑您正在建造一座新建筑。

你建造地下室,一楼,二楼。 在摧毁时,你会摧毁二楼,一楼和地下室。

同样,对象的构造/破坏发生在C ++中。

希望这有助于理解。

答案 1 :(得分:1)

内存分配独立于调用构造函数和析构函数。构造派生对象时,将分配整个类型的内存。

然后,调用它的构造函数(不是基础构造函数)。但是,此构造函数首先调用基类构造函数。默认情况下,将调用base的默认构造函数,但您可以指定如何在派生构造函数中调用基本构造函数。如果不存在这样的并且您没有指定如何构造基类,那么这是一个编译错误。

class Base {
    int x;
public:
    Base() : x(42) {}
};

class Derived : public Base {
    int y;
public:
    Derived() : Base(), y(1337) {}
    // is the same as: Derived() {}
};

这里,Base()是不可能的(它不提供默认构造函数):

class Base {
    int x;
public:
    Base(int x) : x(x) {}
};

class Derived : public Base {
    int y;
public:
    Derived() : Base(),   y(1337) {}  // <-- error!
    Derived() : Base(42), y(1337) {}  // <-- ok!
};

正如您所看到的,从技术上讲,派生的构造函数是第一个被调用的构造函数。但是因为它在一开始就调用了基本构造函数,所以它实际上是另一种方式:构造了基类,然后派生类“添加”它自己的东西。

破坏只是反过来了:派生类的一个实例需要在基础被破坏之前清理它添加到基类的东西。

将此视为建造房屋:您必须首先建造基地,然后添加故事,最后是屋顶。在破坏时,首先移除屋顶,然后移除故事,最后摆脱基地。

答案 2 :(得分:0)

这是另一条规则的结果:在进入派生构造函数的主体之前调用所有基础构造函数,并在离开派生析构函数的主体后调用所有基础析构函数。

考虑这个(过度简化的)代码:

class BaseMember
{
public:
    bool Pred();
};

class DerivedMember
{
public:
    void DoFirst(bool);
    void DoLast(bool);
};

class Base
{
public:
    Base() {baseMember=new BaseMember;}
    ~Base(){delete baseMember;}
protected:
    BaseMember *baseMember;
};

class Derived: public Base
{
public:
    Derived()
    {
        derivedMember=new DerivedMember;
        derivedMember->DoFirst(baseMember->Pred());
    }
    ~Derived()
    {
        derivedMember->DoLast(baseMember->Pred());
        delete derivedMember;
    }
protected:
    DerivedMember *derivedMember;
};

我们可以编写类似的东西,因为我们依赖于Base完全构造(所有Base的基础构造函数都已完成,所有Base成员构造函数也已完成,Base的构造函数体已完成),然后我们才进入Derived构造函数体。通过这种方式,我们可以使用知道它们构建的所有基础成员。当我们进入Derived析构函数的主体时,我们知道没有任何东西被破坏了,所以 我们可以使用所有基地的成员知道他们仍然存在,并将在以后被破坏。

构造函数/析构函数调用层次结构是此逻辑的结果。如果我们在输入Derived()的主体之前调用Base构造函数,并且Base本身是从BaseOfBase派生的,那么Base构造函数将在进入Base()的主体之前调用BaseOfBase()构造函数。如果我们在离开~Derived()析构函数体之后调用~Base()析构函数,那么~BaseOfBase()析构函数将在~Base()析构函数完成后调用。

在C ++中,多重继承(和虚拟继承)会进一步复杂化调用层次结构,但同样的逻辑也适用于这种情况。