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

jot*_*tik 25 c++ arrays inheritance terminology pointer-arithmetic

比方说,我有一个类Derived从类派生Basesizeof(Derived) > sizeof(Base).现在,如果分配一个这样的数组Derived:

Base * myArray = new Derived[42];
Run Code Online (Sandbox Code Playgroud)

然后尝试使用访问n-th对象

doSomethingWithBase(myArray[n]);
Run Code Online (Sandbox Code Playgroud)

然后,由于Base从无效位置访问,这可能(但不总是)导致未定义的行为.

这种编程错误的正确术语是什么?它应该被视为对象切片的情况吗?

Joh*_*nck 26

它根本不是切片,而是未定义的行为,因为你正在访问一个Derived不存在的对象(除非你很幸运并且大小排成一行,在这种情况下它仍然是UB,但无论如何都可以做一些有用的事情).

这是一个失败的指针算法的简单案例.


jxh*_*jxh 19

这不是对象切片.

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

一种"数组衰变bug".

在分配中引入的缺陷new Derived[42],以myArray可以是的变化阵列衰变错误.

在这种类型的bug的真实例子中,有一个实际的数组:

Derived x[42];
Base *myArray = x;
Run Code Online (Sandbox Code Playgroud)

之所以引入这个问题,是因为一个数组Derived衰减成指针,Derived其值等于其第一个元素的地址.衰减允许指针分配正常工作.此衰减行为继承自C,这是一种语言设计功能,允许数组"通过引用传递".

这导致我们更糟糕地体现了这个bug.此功能为数组语法提供C和C++语义,将数组函数参数转换为指针参数的别名.

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

Derived d[42];
foo(d);          // Boom.
Run Code Online (Sandbox Code Playgroud)

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

检测并避免错误.

使用智能指针.

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

std::unique_ptr<Base[]> myArray = new Derived[42];
Run Code Online (Sandbox Code Playgroud)

这会产生编译时错误,因为unique_ptrs构造函数是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]);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

std::vector<Derived> v(42);
std::vector<std::reference_wrapper<Base>> myArray(v.begin(), v.end());
Run Code Online (Sandbox Code Playgroud)


n. *_* m. 11

这不是任何方式的对象切片.

对象切片完全由C++标准定义.它可能违反了面向对象的设计原则或其他任何原则,但它并不违反C++规则.

此代码违反了5.7 [expr.add]第7段:

对于加法或减法,如果表达式PQ类型为"指向cv的 指针T",其中T与cv-unqualified数组元素类型不同,则行为未定义.[注意:特别是,当数组包含派生类类型的对象时,指向基类的指针不能用于指针算术. - 注意].

数组下标运算符被定义为等效于指针运算,5.2.1 [expr.sub]第1段:

表达式E1[E2](根据定义)相同*((E1)+(E2))

  • @jotik我的意思是对象切片不违反任何C++规则.没有未定义的行为. (2认同)

eer*_*ika 8

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