使用继承时构造函数/析构函数调用的顺序
Order of Constructor/Destructor Calls When Using Inheritance
我试图通过编写一些示例代码并尝试遵循程序流程来理解调用构造函数和析构函数的顺序。在大多数情况下,我能够理解(在需要时在谷歌的帮助下(。然而,在一个特殊的情况下,我遇到了一些障碍。
这是我正在使用的程序:
#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;
}
这是我得到的输出:
$ g++ test_vd_se.cpp -o test; ./test
Calling constructor of base class:
Calling constructor of child class:
Calling destructor of child class:
Calling destructor of base class:
Base Class
Calling destructor of base class:
编译器似乎首先创建一个基类和子类(这是意料之中的(,但是它继续销毁这两个类,但它可以从基类调用成员函数并继续再次销毁基类。
如果有人能解释为什么按这个顺序调用函数,我将不胜感激。
这里的问题是你正在切片对象。
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;
}
或者,如果您无法访问智能指针:
int main()
{
baseC* c3 = new childC();
std::cout << c3->getName() << std::endl;
delete c3;
}
你会得到:
Calling constructor of base class:
Calling constructor of child class:
Child Class
Calling destructor of child class:
Calling destructor of base class:
现场示例
我们还需要制作~baseC()
virtual
以便调用正确的析构函数。
virtual ~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;}
您还将注意到,现在打印Child Class
而不是Base Class
因为现在我们有一个指针动态调度启动并调用正确的虚函数。
"异常"来自以下赋值:
baseC c3 = childC();
首先创建一个临时childC
,按顺序从上到下调用构造函数:
Calling constructor of base class:
Calling constructor of child class:
然后进行赋值,从而创建一个baseC
对象。但这一次,调用的不是构造函数,而是默认的复制构造函数。这就是为什么我们没有再次观察Calling constructor of base class:
(用于对象c3
的构造(。为了证明这一点,请尝试将复制构造函数添加到 baseC 类中:
baseC(const baseC& other) { std::cout << "Calling Copy-constructor of base class: " << std::endl; }
使用相同的主函数,您将在输出中观察该句子两次:
Calling constructor of base class:
Calling constructor of child class:
**Calling copy-constructor of base class:**
Calling destructor of child class:
Calling destructor of base class:
Base Class
Calling destructor of base class:
最后,临时子对象被销毁,因此自下而上调用析构函数。
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
最后,将析构函数声明为虚拟始终是更安全和良好的做法。
- 析构函数调用
- 在具有向量的类构造函数中进行析构函数调用
- 从 c++ 中派生类的析构函数调用虚函数
- C++析构函数调用两次,堆栈分配的复合对象
- C++ 在析构函数调用之前删除的动态成员数组
- 析构函数调用c++中的一个向量
- Singleton模式中的手动析构函数调用:调用多次
- 从内部类的析构函数调用虚拟函数
- 与 boost odeint 集成期间的析构函数调用
- 堆栈展开如何与析构函数调用有关?
- C++:优化析构函数调用
- 以逗号分隔的表达式中的析构函数调用
- GCC 9.1 返回 void& 作为显式析构函数调用的结果类型。这是一个错误吗?
- 从C++中的虚拟析构函数调用虚拟方法
- 从指针返回对象时出现意外的析构函数调用
- 使用 decltype 显式析构函数调用
- C++析构函数调用了错误的对象
- 了解虚拟函数和析构函数调用
- 多重继承析构函数调用他自己和父析构函数?c++
- 析构函数调用表单不适当的库