在GMan's answer here中,restore_base
类的析构函数不是virtual
,所以我一直想知道它是如何工作的。通常,您希望在对象超出范围之后仅执行restorer_base
的析构函数,但似乎实际调用了派生的restorer_holder
析构函数。有人想开导我吗?
答案 0 :(得分:18)
需要虚拟析构函数的标准情况是
void foo()
{
scoped_ptr<Base> obj = factory_returns_a_Derived();
// ... use 'obj' here ...
}
不的标准情况是
void foo()
{
Derived obj;
// ... use 'obj' here ...
}
GMan的代码做了一些有点棘手的事情,结果证明与第二种情况相同:
void foo()
{
Base& obj = Derived();
// ... use 'obj' here ...
}
obj
是一个简单的参考;通常,它根本不会触发析构函数。但它是从一个匿名临时对象初始化的,其静态类型 - 编译器已知 - 为Derived
。当该对象的生命周期结束时,编译器将调用Derived
析构函数。通常,匿名临时对象会在创建它的表达式的末尾消亡,但临时对象初始化引用有一个特殊情况:它们一直存在,直到引用本身死亡,这是范围的结束。所以你得到伪 - scoped_ptr
行为,你不需要虚拟析构函数。
编辑:由于现在已经出现过两次:参考不必须为const
才能应用此特殊规则。 C + 98 [class.temporary] / 5:
第二个上下文[其中临时对象在其末尾未被销毁 full-expression]是引用绑定到临时的时候。临时到哪 引用是绑定的,也可以是作为子对象的完整对象的临时对象 临时绑定的内容在参考的生命周期 ...
中持续存在
强调我的。此语言中未提及const
,因此引用不必是const
。
编辑2:标准中的其他规则禁止为非左值的临时对象创建非常量引用。我怀疑至少有一些临时对象是左值,但我不确定。无论如何,不会影响此规则。对于临时对象的非常量引用延长其生命周期仍然是正式的,即使没有严格符合C ++程序也无法创建这样的引用。这可能看起来很荒谬,但你应该从字面上和迂腐地阅读这个标准。每个单词都很重要,每个单词都不重要。