这应该被称为对象切片的一些特例吗?
Should this be called some special case of object slicing?
假设我有一个 class Derived
派生自 class Base
而 sizeof(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());
假设我有一个 class Derived
派生自 class Base
而 sizeof(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
orQ
have type “pointer to cvT
”, whereT
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());