通过值将子类对象传递给采用超类对象的函数是否是明确定义的行为?

时间:2014-04-16 20:03:31

标签: c++ inheritance

我已经查看过herehere这个主题的相关问题,它们都描述了对象切片,但没有一个解决它是否安全可靠可预测

是否有来自标准版或编译器的保证,如果我按值将子类对象传递给想要超类的方法,则切掉的部分完全成员子类,我将能够使用切片的超类对象而不用担心未定义的行为?

2 个答案:

答案 0 :(得分:6)

是的,它是安全,可靠和可预测的,因为标准很好地定义了它(它只是从派生类对象中复制构造一个基类对象)。

但不,它不安全,不应该依赖它,并且通常被视为不可预测,因为你的读者不会知道发生了什么。当其他人稍后尝试修改您的代码(包括您自己的未来)时,这将触发批次的错误。它基本上是一个禁忌,与goto语句的方式非常相似,它的定义非常明确,可靠且可预测。

答案 1 :(得分:3)

当他们说“部件被切掉”时,他们并不意味着这些部件以某种方式“消失”:他们都意味着这些部件不会被复制到为相应参数呈现给您的函数的对象中。从这个意义上讲,对象切片并不危险或定义不明确。

那里发生的事情相当简单:为了构造您通过值传递的参数的值,用于实际参数的派生类的对象被赋予基类的构造函数以进行复制。一旦复制构造函数完成了它的工作,你就有了一个基类的对象。

此时,您拥有基类的完全正常运行的对象。编译器会防止您按值接受具有纯虚拟成员的类,因此您无法将对象切片为缺少纯虚函数的实例。

更重要的一个问题是你是否希望隐式地进行切片行为:编译器在这里没有做任何你在代码中无法做到的事情:

void foo(bar b) {
    ... // Payload logic
}

为您提供与

相同的功能
void foo(const bar &r) {
    bar b(r);
    ... // Payload logic
}

使用第一个片段,很容易错过在类型名称后缺少&符号这一事实,导致读者认为多态行为被保留,而实际上却丢失了。第二个代码段对于维护代码的人来说更容易理解,因为它会明确地制作副本。