7

我有以下基本代码:

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

YX通过函数= OK

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

YX通过三元运算符= 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函数运行两次?
4

2 回答 2

11

您缺少的是您的临时Y文件将按切片复制构建到X绑定到您的 const 引用的隐藏临时文件中。就是你看到的最后一个析构函数,也解释了为什么Y比预期的更早被破坏。制作此副本的原因是三元运算符的“返回”只是一种类型。AnX不可能被视为常用类型,因此会产生额外的临时Y对象。XX

请注意,这与“Just Y”测试用例不同,因为在这种情况下Y会创建一个临时对象,然后立即尝试绑定到const X&允许的对象。在三元情况下,运算符本身将中间切片引入运算符操作数的公共对象类型,在这种情况下为X

相信您可以通过转换为父引用来避免临时切片,但我无权访问 C++11 编译器来测试它(除了问题中有些不完整的代码):

const X& var = false ? X{"temporaryX"} : static_cast<const X&>(Y{"temporaryY"});
于 2013-09-20T14:28:16.317 回答
5

在范围结束之前调用 Y 的析构函数?

因为Y创建了一个类型的对象,所以它必须被销毁。因为它是一个临时对象,所以它必须在表达式的末尾(在 之后;)被销毁。

X::foo() 被调用而不是 Y::foo()?

由于对象切片,它调用该X:foo方法。对象被分割成一个临时X对象。

这在第 5.16/3 节的标准中进行了解释。

X 的析构函数运行两次?

对临时对象X调用一次析构函数,对.Yvar


于 2013-09-20T14:28:45.513 回答