这应该被称为对象切片的一些特例吗?

Should this be called some special case of object slicing?

假设我有一个 class Derived 派生自 class Basesizeof(Derived) > sizeof(Base)。现在,如果像这样分配一个 Derived 的数组:

Base * myArray = new Derived[42];

然后尝试使用

访问第n个对象
doSomethingWithBase(myArray[n]);

那么这可能(但并非总是)由于从无效位置访问 Base 而导致未定义的行为。

这种编程错误的正确术语是什么? 是否应该考虑 object slicing 的情况?

它根本不是切片,而是未定义的行为,因为您正在访问 none 存在的 Derived 对象(除非您很幸运并且大小对齐,在这种情况下它仍然是 UB,但无论如何可能会做一些有用的事情。

这是一个简单的指针运算失败的例子。

这不是切片的情况,尽管它非常相似。切片定义明确。由于非法指针运算,这只是未定义的行为(总是,不仅仅是可能)。

这绝不是对象切片。

C++ 标准完美定义了对象切片。这可能违反了面向对象的设计原则或其他什么,但它并不违反C++规则。

此代码违反了5.7 [expr.add] paragraph 7

For addition or subtraction, if the expressions P or Q have type “pointer to cv T”, where T is different from the cv-unqualified array element type, the behavior is undefined. [Note: In particular, a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type. —end note].

定义数组下标运算符等价于指针运算,5.2.1 [expr.sub] paragraph 1:

The expression E1[E2] is identical (by definition) to *((E1)+(E2))

这不是对象切片。

如前所述,索引 myArray 不会导致对象切片,但会导致未定义的行为,因为索引到 Derived 的数组就好像它是 Base 的数组一样。

一种"array decay bug".

new Derived[42] 分配给 myArray 时引入的错误可能是 数组衰减错误的变体

在此类错误的真实实例中,有一个实际数组:

Derived x[42];
Base *myArray = x;

引入问题是因为 Derived 的数组衰减为指向 Derived 的指针,其值等于其第一个元素的地址。衰减允许指针分配正常工作。这种衰减行为是从 C 继承的,这是一种允许数组 "passed by reference".

的语言设计特性

这让我们想到了这个错误的更糟糕的化身。此功能为数组语法提供 C 和 C++ 语义,将数组函数参数转换为指针参数的别名。

void foo (Base base_array[42]) {
    //...
}

Derived d[42];
foo(d);          // Boom.

然而,new[]实际上是一个重载运算符,returns指向分配数组对象开头的指针。所以它不是数组衰减的真实实例(即使使用了数组分配器)。但是bug症状是一样的,new[]的本意是得到一个Derived.

的数组

检测并避免错误。

使用智能指针。

可以通过使用智能指针对象而不是管理原始指针来避免此类问题。例如,unique_ptr 的类似编码错误看起来像:

std::unique_ptr<Base[]> myArray = new Derived[42];

这会产生编译时错误,因为 unique_ptr 的构造函数是 explicit

使用容器,也许std::reference

或者,您可以避免使用 new[],而使用 std::vector<Derived>。然后,您会强迫自己设计一个不同的解决方案来将此数组发送到仅 Base 知道的框架代码。可能是模板函数。

void my_framework_code (Base &object) {
    //...
}

template <typename DERIVED>
void my_interface(std::vector<DERIVED> &v) {
    for (...) {
        my_framework_code(v[i]);
    }
}

或者,通过使用 std::reference_wrapper<Base>

std::vector<Derived> v(42);
std::vector<std::reference_wrapper<Base>> myArray(v.begin(), v.end());