为什么在对基类的 const 引用上调用派生类的析构函数?

2022-01-05 00:00:00 inheritance reference c++

在 GMan 的回答中,restore_base 类的析构函数不是 virtual,所以我一直想知道它到底是如何工作的.通常,您希望 restorer_base 的析构函数仅在对象超出范围后才执行,但似乎派生的 restorer_holder 析构函数确实被调用了.有谁愿意教教我吗?

In GMan's answer here, the destructor of the restore_base class isn't virtual, so I keep wondering how exactly that works. Normally you'd expect the destructor of restorer_base to be executed only, after the object goes out of scope, but it seems that the derived restorer_holder destructor is really called. Anyone care to enlighten me?

推荐答案

需要虚拟析构函数的标准情况是

The standard case where you need a virtual destructor is

void foo()
{
   scoped_ptr<Base> obj = factory_returns_a_Derived();

   // ... use 'obj' here ...
}

你不的标准情况是

void foo()
{
   Derived obj;

   // ... use 'obj' here ...
}

GMan 的代码做了一些更棘手的事情,结果与第二种情况等效:

GMan's code is doing something a little trickier, that turns out to be equivalent to the second case:

void foo()
{
   Base& obj = Derived();

   // ... use 'obj' here ...
}

obj 是一个裸引用;通常,它根本不会触发析构函数.但是它是从一个匿名临时对象初始化的,它的静态类型――编译器知道――是Derived.当那个对象的生命周期结束时,编译器将调用Derived析构函数.通常,匿名临时对象在创建它的 表达式 结束时死亡,但临时对象初始化引用有一个特殊情况:它们一直存在到引用本身死亡,这是作用域的结尾.因此,您获得了伪scoped_ptr 行为,并且不需要虚拟析构函数.

obj is a bare reference; normally, it would not trigger destructors at all. But it's initialized from an anonymous temporary object whose static type -- known to the compiler -- is Derived. When that object's lifetime ends, the compiler will call the Derived destructor. Normally an anonymous temporary object dies at the end of the expression that created it, but there's a special case for temporaries initializing a reference: they live till the reference itself dies, which here is the end of the scope. So you get pseudo-scoped_ptr behavior and you don't need a virtual destructor.

因为这已经出现了两次:引用不必必须是const才能应用这个特殊规则.C+98 [class.temporary]/5:

Since this has now come up twice: The reference does not have to be const for this special rule to apply. C+98 [class.temporary]/5:

第二个上下文[其中一个临时对象在结束时不被销毁full-expression] 是 一个引用 绑定到一个临时的.暂时的引用被绑定或作为完整对象的临时对象到子对象临时绑定的对象在引用的生命周期内持续 ...

The second context [in which a temporary object is not destroyed at the end of the full-expression] is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object to a subobject of which the temporary is bound persists for the lifetime of the reference ...

强调我的.在这种语言中没有提到 const,所以引用不一定是 const.

Emphasis mine. There is no mention of const in this language, so the reference does not have to be const.

EDIT 2: 标准中的其他规则禁止创建对不是左值的临时对象的非常量引用.我怀疑至少一些临时对象是左值,但我不确定.无论如何,这不会影响此规则.对临时对象的非常量引用可以延长它们的生命周期,这在形式上仍然是正确的,即使没有严格遵守的 C++ 程序能够创建这样的引用.这可能看起来很荒谬,但您应该从字面上和迂腐地阅读标准语.每一个字都很重要,每一个不存在的字都很重要.

EDIT 2: Other rules in the standard prohibit creation of non-const references to temporary objects that are not lvalues. I suspect that at least some temporary objects are lvalues, but I don't know for certain. Regardless, that does not affect this rule. It would still be formally true that non-const references to temporary objects prolong their lifetime even if no strictly conforming C++ program could ever create such a reference. This might seem ridiculous, but you're supposed to read standardese this literally and pedantically. Every word counts, every word that isn't there counts.

相关文章