我有以下基本代码:
struct X {
X(const char* descr) {...}
~X() {...} // Not virtual
virtual void foo() const {...}
};
struct Y : public X {
Y(const char* descr) {...}
~Y() {...} // Not virtual
virtual void foo() const {...}
};
const X& factory() {
static X sampleX{"staticX"};
static Y sampleY{"staticY"};
return X or Y depending of the test case;
};
4个测试用例:
只需Y
=确定
const X& var = Y{"temporaryY"};
var.foo();
结果:
X::X() // base temporaryY
Y::Y() // temporaryY
Y::foo()
Y::~Y() // temporaryY
X::~X() // base temporaryY
只需X
=确定
const X& var = X{"temporaryX"};
var.foo();
结果:
X::X() // temporaryX
X::foo()
X::~X() // temporaryX
Y
或X
通过功能 =确定
const X& var = factory();
var.foo();
结果:
X::X() // staticX
X::X() // base staticY
Y::Y() // staticY
X::foo() or Y::foo()
Y::~Y() // staticY
X::~X() // base staticY
X::~X() // staticX
Y
或X
通过三元运算符 = WTF?!
const X& var = false ? X{"temporaryX"} : Y{"temporaryY"};
var.foo();
结果:
X::X() // base temporaryY
Y::Y() // temporaryY
Y::~Y() // temporaryY
X::~X() // base temporaryY
X::foo()
X::~X() // base temporaryY
有人可以解释为什么七个地狱:
Y
的析构函数?X::foo()
被调用而不是Y::foo()
?X
的析构函数运行两次?答案 0 :(得分:11)
您缺少的是您的临时Y
正在逐层复制构造到绑定到您的const引用的隐藏临时X
。 那个就是你看到的最终析构函数,也解释了为什么Y
比预期更早被破坏。这个副本的原因是三元运算符的“返回”只是一种类型。 X
不可能被视为Y
,因此X
是要使用的常见类型,从而导致额外的临时X
对象。
请注意,这与“Just Y”测试用例不同,因为在该实例中创建了Y
临时值,然后立即尝试绑定到允许的const X&
。在三元情况下,运算符本身会引入运算符操作数的公共对象类型的中间切片,在本例中为X
。
我相信你可以通过强制转换为父引用来避免临时切片,但是我无法访问C ++ 11编译器来测试它(除了有些不完整的代码之外)问题):
const X& var = false ? X{"temporaryX"} : static_cast<const X&>(Y{"temporaryY"});
答案 1 :(得分:5)
在范围结束之前调用Y的析构函数?
因为创建了Y
类型的对象,所以必须将其销毁。因为它是一个临时对象,所以必须在表达式的末尾(;
之后)销毁它。
调用X :: foo()而不是Y :: foo()?
由于object slicing,它会调用X:foo
方法。该对象被切成一个临时的X
对象。
这在§5.16/ 3的标准中有解释。
X的析构函数运行两次?
X
析构函数为Y
临时对象调用一次,为var
调用一次。