Xeo*_*Xeo 36 c++ undefined-behavior dynamic-arrays delete-operator
我在C++ 03标准中找到了以下片段5.3.5 [expr.delete] p3:
在第一个备选(删除对象)中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为操作数的动态类型的基类,静态类型应具有虚拟析构函数或者行为未定义.在第二个备选(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义.
快速回顾静态和动态类型:
struct B{ virtual ~B(){} };
struct D : B{};
B* p = new D();
Run Code Online (Sandbox Code Playgroud)
静态类型p是B*,而动态类型*p是D,1.3.7 [defns.dynamic.type]:
[ 例如:如果一个
p静态类型为"指向"的指针class B指向一个对象class D,B该表达式的动态类型*p是"D."]
现在,再次查看顶部的引用,这意味着如果我正确的话,下面的代码会调用未定义的行为,无论是否存在virtual析构函数:
struct B{ virtual ~B(){} };
struct D : B{};
B* p = new D[20];
delete [] p; // undefined behaviour here
Run Code Online (Sandbox Code Playgroud)
我是否以某种方式误解了标准中的措辞?我忽略了什么吗?为什么标准将此指定为未定义的行为?
Luc*_*ton 32
Base* p = new Base[n]创建一个n大小的Base元素数组,p然后指向第一个元素.Base* p = new Derived[n]但是,创建一个n大小的Derived元素数组.p然后指向Base 第一个元素的子对象.但是,p它并不引用数组的第一个元素,这是有效delete[] p表达式所需要的.
当然,delete [] p在这种情况下,可以强制执行(然后实施)Do The Thing™.但它需要什么?实现必须注意以某种方式检索数组的元素类型,然后在道德上检索dynamic_cast p此类型.然后delete[]就像我们已经做的那样做一个简单的问题.
问题在于,每次多态元素类型的数组都需要这样,无论是否使用多态.在我看来,这不符合C++的理念,即不为你不使用的东西付费.但更糟糕的是:启用多态的功能delete[] p简直无用,因为p在您的问题中几乎没用.p是一个指向元素子对象的指针而不再是; 否则它与阵列完全无关.你当然可以这样做p[i](为i > 0它).所以delete[] p不起作用并不是不合理的.
总结一下:
数组已经有很多合法用途.通过不允许数组以多态方式运行(无论是整体还是仅用于delete[]),这意味着具有多态元素类型的数组不会因这些合法用途而受到惩罚,这符合C++的理念.
另一方面,如果需要具有多态行为的数组,则可以根据我们已经实现的数组来实现.
sth*_*sth 12
将一个派生数组作为基数数组处理是错误的,而不仅仅是在删除项目时.例如,即使只访问元素通常也会导致灾难:
B *b = new D[10];
b[5].foo();
Run Code Online (Sandbox Code Playgroud)
b[5]将使用大小B来计算要访问的内存位置,如果B且D具有不同的大小,则不会产生预期的结果.
就像一个std::vector<D>无法转换为a std::vector<B>,指针D[]不应该转换为a B*,但由于历史原因它无论如何都会编译.如果std::vector改为使用a ,则会产生编译时错误.
关于此主题的C++ FAQ Lite中也解释了这一点.
因此delete在这种情况下导致未定义的行为,因为以这种方式处理数组已经是错误的,即使类型系统无法捕获错误.