0

请考虑以下代码:

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

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

我想了解这件事的真正原因。即,Base变量不能在Derived不切片的情况下保存对象的原因。

我的假设是,这样做的原因是Base对象和Derived对象的大小可能不同,因此我们不能保证能够将整个Derived对象存储在Base变量中。ABase可能占用 4 个字节,而 aDerived是 7 个字节。所以我们决定总是对派生对象进行切片以适应基本类型的大小。

我们可以使用指针来做到这一点,因为它们都占用相同数量的内存。

这个假设正确吗?如果不是,那么切片的实际原因是什么?

4

2 回答 2

2

不,在你的例子中,切片的原因是不同的。

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

假设您DerivedA某种方法可以将对象中包含的所有信息存储在对象中Base(尽管这不太可能),您可以将赋值运算符重载为Base::operator=(const DerivedA&),实现您自己的赋值语义,并且上述方法可以正常工作。

于 2014-10-16T22:07:08.610 回答
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
    }
};

即使使用按位复制(for std::is_trivially_copyable<T>),大小也将是 BaseType,并且正如您所指出的,它比必要的要小,并且会截断数据。

希望这可以帮助。

于 2014-10-16T22:02:50.187 回答