关于实际调用的析构函数有一个主要含义。检查 Gotw88、Q3 和 A3。我把所有东西都放在一个小测试程序中(Visual-C++,所以请原谅 stdafx.h)
// Gotw88.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
class A
{
protected:
bool m_destroyed;
public:
A() : m_destroyed(false) {}
~A()
{
if (!m_destroyed)
{
std::cout<<"A destroyed"<<std::endl;
m_destroyed=true;
}
}
};
class B : public A
{
public:
~B()
{
if (!m_destroyed)
{
std::cout<<"B destroyed"<<std::endl;
m_destroyed=true;
}
}
};
B CreateB()
{
return B();
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<<"Reference"<<std::endl;
{
const A& tmpRef = CreateB();
}
std::cout<<"Value"<<std::endl;
{
A tmpVal = CreateB();
}
return 0;
}
这个小程序的输出如下:
Reference
B destroyed
Value
B destroyed
A destroyed
这里是设置的一个小解释。B 派生自 A,但两者都没有虚拟析构函数(我知道这是一个 WTF,但在这里很重要)。CreateB() 按值返回 B。Main 现在调用 CreateB 并首先将此调用的结果存储在类型 A 的 const 引用中。然后调用 CreateB 并将结果存储在类型 A 的值中。
结果很有趣。首先 - 如果您按引用存储,则调用正确的析构函数(B),如果您按值存储,则调用错误的析构函数。其次 - 如果你存储在一个引用中,析构函数只被调用一次,这意味着只有一个对象。按值导致 2 次调用(对不同的析构函数),这意味着有 2 个对象。
我的建议 - 使用 const 参考。至少在 Visual C++ 上,它可以减少复制。如果您不确定您的编译器,请使用并调整此测试程序来检查编译器。如何适应?添加复制/移动构造函数和复制赋值运算符。
我快速为 A 类和 B 类添加了复制和赋值运算符
A(const A& rhs)
{
std::cout<<"A copy constructed"<<std::endl;
}
A& operator=(const A& rhs)
{
std::cout<<"A copy assigned"<<std::endl;
}
(对于 B 也一样,只需将每个大写字母 A 替换为 B)
这导致以下输出:
Reference
A constructed
B constructed
B destroyed
Value
A constructed
B constructed
A copy constructed
B destroyed
A destroyed
这证实了上面的结果(请注意,A 构造的结果来自 B 被构造为 B 是从 A 派生的,因此每当调用 Bs 构造函数时都会调用 As 构造函数)。
附加测试:Visual C++ 也接受非常量引用,其结果(在本例中)与 const 引用相同。此外,如果您使用 auto 作为类型,则(当然)会调用正确的析构函数,并且会启动返回值优化,最终它与 const 引用的结果相同(但当然,auto 的类型为 B 而不是 A) .