关于下面的示例代码,为什么基类的析构函数被调用两次?
class Base {
public:
Base() {
std::cout << "Base::Base()" << std::endl;
}
~Base() {
std::cout << "Base::~Base()" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived::Derived()" << std::endl;
}
~Derived() {
std::cout << "Derived::~Derived()" << std::endl;
}
};
int main() {
Base a = Derived();
return EXIT_SUCCESS;
}
以下是程序运行时输出的示例:
Base::Base()
Derived::Derived()
Derived::~Derived()
Base::~Base()
Base::~Base()
答案 0 :(得分:16)
所发生的事情称为切片。使用类型为Base
的对象初始化类型为Derived
的对象。由于类型Derived
的任何对象都包含类型Base
的对象(称为“基类子对象”),因此将有两个Base
个对象和一个{{1}整个程序中存在的对象。 Derived对象(及其类型为Derived
的基类子对象)仅在初始化时存在,而剩余的Base
对象存在直到Base
结束。
由于有两个Base对象和一个Derived对象,因此您还将看到另外一个Base destructors运行。
答案 1 :(得分:7)
正在使用复制构造函数。如果您想查看正在发生的事情,请检查复制构造函数:
Base( const Base & ) {
std::cout << "Base::Base( const Base &)" << std::endl;
}
和Derived类似。
请注意,这与析构函数不是虚拟的无关。
答案 2 :(得分:4)
当您在Derived()
中说main()
时,它会创建一个临时对象,然后将其复制到对象a中。因此有两个对象,因为析构函数被调用两次。另外,正如其他人指出你的基类析构函数应该是虚拟的。
答案 3 :(得分:2)
因为您在使用它复制构建Derived
之前创建了a
类型的临时文件。所以这基本上就是这样:
Derived d(); // Your temporary of type Derived is created
Base a(d); // The temporary is used to call a's copy constructor
d.Derived::~Derived(); // The temporary is destroyed, calling both ~Derived and ~Base
a.Base::~Base(); // The nonvirtual destructor on a is called, so ~Base is called, but not ~Derived
除了开头不必要的复制(编译器可能会优化掉)之外,实际的错误就是~Base不是虚拟的。
修改强> 哎呀,完全错过了litb指出的切片。请改为阅读他的答案:)
答案 4 :(得分:1)
添加以下内容将使程序更加清晰:
Base(const Base& base){
std::cout << "Base::Base(const Base& base)" << std::endl;
}
编译器会自动为您创建一个复制构造函数。通过自己定义,(并通过添加打印),您可以看到构造函数和析构函数的数量匹配
Base::Base()
Derived::Derived()
Base::Base(const Base& base)
Derived::~Derived()
Base::~Base()
Base::~Base()
答案 5 :(得分:0)
答案 6 :(得分:0)
你有一个堆栈变量和一个临时变量 - 总共构造了两个对象 - 因此,析构函数被调用两次是合乎逻辑的。
答案 7 :(得分:0)
1)构建Derived类型的临时对象(调用Derived :: Derived()和Base :: Base())
2)临时对象被复制到“a”
3)临时对象被销毁(Derived :: ~Derived()和Base :: ~Base()被调用)
4)返回EXIT_SUCCESS;
5)“a”被销毁,因此Base ::〜Base()被称为