请考虑以下代码:
Base b;
if (something)
b = DerivedA();
else
b = DerivedB();众所周知,在这种情况下,会发生‘切片’:在C++中,我们不能将基类型的变量指定为派生类型的对象;对象将从基类型中未定义的任何对象中“切片”。(如果我们想做这样的事情,我们必须使用指针或引用)。
我想了解这件事的实际原因。也就是说,Base变量不能在不切片的情况下保存Derived对象的原因。
我的假设是,这样做的原因是,Base对象和Derived对象的大小可能不一样,因此我们无法保证能够将整个Derived对象存储在Base变量中。一个Base可能占4个字节,而一个Derived占7个字节。因此,我们总是对派生对象进行切片,以适应基类型的大小。
我们可以用指针来完成这一任务,因为它们都占用了相同的内存。
这个假设正确吗?如果没有,切片的实际原因是什么?
发布于 2014-10-16 22:02:50
问题在于复制和移动语义(复制构造函数、复制分配等)。您正在获得这些元素的副本,但是并不是所有的元素都将被复制。如果您有一个基本指针,就不会有问题。
如果您有一个完全填充的DerivedA对象,并将其分配给本地堆栈基类型,则将使用副本赋值,而任何派生元素值都将被丢弃。
在编写副本构造函数时考虑一下。除了当前班的成员外,您还做其他的工作吗?您如何知道从当前类派生出来的内容,以及要做什么工作?试图这样做将是非常糟糕的。
class BaseType
{
private:
int m_i;
public:
explicit BaseType(BaseType const & other) // copy ctor
{
m_i = other.m_i; // bitwise copy or memberwise copy will suffer the same issue
// what else is there to do?
// BaseType has no knowledge of any other members
}
BaseType & BaseType::operator=(BaseType const & other) // copy assignment
{
m_i = other.m_i;
// what else is there to do?
// BaseType has no knowledge of any other members
}
};即使是按位复制(对于std::is_trivially_copyable<T>),大小也将为BaseType,正如您所指出的,比需要的要小,并且会截断数据。
希望这能有所帮助。
发布于 2014-10-16 22:07:08
不是的。在您的例子中,切片的原因是不同的。
在Base b;行中,您为堆栈上Base类型的对象分配空间,并且已经调用了它的默认构造函数。因此,在if-statement的每个分支中,发生的是对b的赋值,它通过赋值操作符实现,通常带有签名的Base::operator=(const Base&)。如果不重载此运算符,则其默认语义为逐字段复制。注意,参数类型是Base (或const Base&),所以只有右边的Base-fields是可见的!
假设您有某种方式将DerivedA对象中包含的所有信息存储在Base对象中(尽管这不太可能),您可以将赋值操作符重载为Base::operator=(const DerivedA&),实现您自己的赋值语义,并且上面的这些都会非常好地工作。
https://stackoverflow.com/questions/26414622
复制相似问题