C++ 使用继承时构造函数/析构函数调用的顺序

C++ 使用继承时构造函数/析构函数调用的顺序,c++,constructor,destructor,C++,Constructor,Destructor,我试图通过编写一些示例代码并尝试遵循程序流程来理解构造函数和析构函数的调用顺序。在大多数情况下,我能够理解(在需要的地方借助谷歌)。然而,在一个特殊的案例中,我遇到了一些障碍 这是我正在使用的程序: #include <iostream> class baseC { public: baseC() { std::cout << "Calling constructor of base class: " << std::endl; }

我试图通过编写一些示例代码并尝试遵循程序流程来理解构造函数和析构函数的调用顺序。在大多数情况下,我能够理解(在需要的地方借助谷歌)。然而,在一个特殊的案例中,我遇到了一些障碍

这是我正在使用的程序:

#include <iostream>
class baseC
{

public:
        baseC() { std::cout << "Calling constructor of base class: " << std::endl; }
        virtual char const * getName(){ return "Base Class";}
        ~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;}
};

class childC : public baseC
{
public:
        childC() { std::cout << "Calling constructor of child class: " << std::endl; }
        char const * getName(){ return "Child Class";}
        ~childC(){ std::cout << "Calling destructor of child class: " << std::endl; }
};

int main()
{
        baseC c3 = childC();
        std::cout << c3.getName() << std::endl;
}
编译器似乎首先创建基类和子类(这是预期的),但是它继续销毁这两个类,并且它可以从基类调用成员函数,然后再次销毁基类


如果有人能解释为什么函数是按这种顺序调用的,我将不胜感激。

这里的问题是您正在切片对象

baseC c3 = childC();
将创建一个临时的
childC
,然后将该对象复制到
c3
。这就是你看到的原因

Calling constructor of base class:  // create base part of temporary
Calling constructor of child class: // create temporary

// the copy happens here but you do not output when copying

Calling destructor of child class:  // destroy base part of temporary
Calling destructor of base class:   // destroy temporary
正确的方法是使用智能指针。如果将
main()
更改为

int main()
{
        auto c3 = std::make_unique<childC>();
        std::cout << c3->getName() << std::endl;
}   

我们还需要将
~baseC()
虚拟化
,以便调用正确的析构函数

virtual ~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;}
virtual~baseC(){std::cout该“异常”来自以下赋值:

baseC c3 = childC();
首先创建一个临时的
childC
,从上到下依次调用构造函数:

Calling constructor of base class: 
Calling constructor of child class:
然后赋值发生,因此创建了一个
baseC
对象。但这次调用的不是您的构造函数,而是默认的复制构造函数。这就是为什么我们没有再次观察调用基类构造函数的
(对于对象的构造
c3
).为了证明这一点,请尝试向baseC类添加副本构造函数:

  baseC(const baseC& other) { std::cout << "Calling Copy-constructor of base class: " << std::endl; }
最后,临时子对象被销毁,因此自下而上调用析构函数

Calling destructor of child class: 
Calling destructor of base class: 
现在
baseC
对象
c3
仍然存在,调用了getName()方法,该方法输出:

Child Class
然后,当变量
c3
超出范围时(
main()
)的末尾,
c3
被销毁:

Calling destructor of base class:
最后,使用
baseC&c3=ChildC();
(使用VS2015编译,我不确定它是否符合C++14标准),它不会创建两个对象,而只创建一个。然后顺序将是:

contruction of baseC
contruction of childC
destruction of childC
destruction of baseC

最后,将析构函数声明为虚拟总是更安全、更好的做法。

如果您将动态内存管理添加到原始代码中,请不要忘记在使用后删除对象。@Revolver\u Ocelot正在添加该命令。谢谢。谢谢!通过上面的答复,我可以更好地理解正在发生的事情。不要忘记声明基类dest构造函数是虚拟的。否则,通过基指针删除将不会调用派生的析构函数。这里最奇怪的是,我们没有观察到两次调用基类构造函数的句子:
。请参阅我的答案以获得解释。您的解决方案将不起作用:@NathanOliver它可以用VS2015完美编译;)这是错误的原因VS215有一个允许非
const
引用的非标准扩展。它是唯一支持此AFAIK的编译器。但无论如何,OP对原始代码中发生的序列tht的解释很感兴趣,这是真的,特别是baseC的构造函数没有出现两次。这对我来说,他们的输出中最奇怪的是:)你的解决方案可能只适用于一个编译器供应商。你不认为你的解决方案应该适用于任何符合标准的编译器吗?
Calling destructor of child class: 
Calling destructor of base class: 
Child Class
Calling destructor of base class:
contruction of baseC
contruction of childC
destruction of childC
destruction of baseC