为什么会发生切片?

时间:2014-10-16 21:58:36

标签: c++ variables object memory object-slicing

请考虑以下代码:

Base b;
if (something)
    b = DerivedA();
else
    b = DerivedB();

众所周知,在这种情况下,会出现“切片”:在C ++中,我们不能将基类型的变量赋予派生类型的对象;对象将被“切片”掉基本类型中未定义的任何内容。 (如果我们想做这样的事情,我们必须使用指针或引用)。

我想了解其实际原因。即,Base变量在不切片的情况下无法保存Derived对象的原因。

我的假设是,原因是Base对象和Derived对象的大小可能不同,因此我们无法保证能够存储整个Derived变量中的Base个对象。 Base可能占用4个字节,而Derived则占用7个字节。所以我们决定总是切割派生对象以适应基本类型的大小。

我们 能够用指针执行此操作,因为它们都占用相同的内存量。

这个假设是否正确?如果没有,切片的实际原因是什么?

2 个答案:

答案 0 :(得分:2)

问题在于复制和移动语义(复制构造函数,复制赋值等)。您将获得元素的副本,但不会复制所有元素。如果你有一个基指针,就没有问题。

如果您有一个完全填充的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,并且正如您所指出的那样,小于必要值,并将截断数据。

希望这有帮助。

答案 1 :(得分:2)

没有。在您的示例中,切片的原因是不同的。

在行Base b;中,为堆栈上的Base类型的对象分配空间,并且已经调用其默认构造函数。因此,在if - 语句的每个分支中,会发生什么是赋值b,它通过赋值运算符实现,通常带有签名{{1 }}。如果不重载此运算符,则其默认语义是逐字段副本。请注意,参数类型为Base::operator=(const Base&)(或Base),因此只有const Base& - 右侧的字段可见!

假设你 以某种方式将Base对象中包含的所有信息存储在DerivedA对象中(虽然这不太可能),你可以重载你的赋值运算符作为Base,实现自己的赋值语义,上面的工作完全正常。