【问题标题】:Constructor and destructor Calls构造函数和析构函数调用
【发布时间】:2013-04-22 19:13:12
【问题描述】:

当我们创建派生类的对象时, 为什么构造函数以自上而下的方式调用(首先是基构造函数,然后是派生构造函数)并且是自下而上调用的析构函数(首先是派生构造函数,然后是基构造函数)

【问题讨论】:

  • 如果类Derived 继承类Base 和类Base 有一个名为BaseProperty 的属性......然后Derived 尝试在其构造函数中设置该属性,您会发生什么?

标签: c# c++ oop


【解决方案1】:

为了向新手解释,假设您正在建造一座新建筑。

你建造地下室,一楼,二楼。 摧毁时,你摧毁 2 楼、1 楼和地下室。

同样的方式,对象的构造/销毁发生在 C++ 中。

希望这有助于理解。

【讨论】:

  • 谢谢,但是为什么这个层次结构。是否有任何特定的理由来遵循这个,就像创建对象时遵循某种数据结构(堆栈)一样。
  • 这样想:继承意味着什么?你正在使用前一个的东西。在地下室之前建造一楼?这没有多大意义。
  • 我同意你的看法,但并不完全满意,因为我只想回答内存分配或类似的任何技术原因。
  • 好的,在这种情况下,请查看@leemes 评论。解释得很好。 :)。
【解决方案2】:

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

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

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!
};

如您所见,从技术上讲,派生构造函数是第一个被调用的构造函数。但由于它在一开始就调用基类构造函数,因此实际上是相反的:基类被构造出来,然后派生类“添加”它自己的东西。

破坏只是相反的方式:派生类的实例需要清理它添加到基类的东西,然后才能破坏基类。

把这简单地想象成建造一座房子:你必须先建造基地,然后添加楼层,最后是屋顶。破坏时,先拆除屋顶,再拆除楼层,最后拆除底座。

【讨论】:

  • 基础构造函数 派生构造函数 派生析构函数 基础析构函数 我只是想要这个层次结构遵循的正当理由
【解决方案3】:

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

考虑这个(过于简单的)代码:

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;
};

我们可以写这样的东西,因为在我们进入 Derived 构造函数体之前,我们依赖于 Base 的完全构造(所有 Base 的构造函数都已完成,所有 Base 成员构造函数也已完成,Base 的构造函数体也已完成)。这样,我们可以在知道它们已构建的情况下使用所有基础的成员。当我们进入 Derived 析构函数的主体时,我们知道还没有任何东西被破坏,所以 我们可以使用所有基地的成员,知道它们仍然存在并且稍后将被破坏。

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

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

【讨论】:

    猜你喜欢
    • 2011-04-16
    • 2021-07-28
    • 1970-01-01
    • 2013-07-07
    • 2013-06-24
    • 2011-04-03
    • 2010-12-16
    • 2017-02-08
    相关资源
    最近更新 更多