【发布时间】:2011-11-04 02:09:21
【问题描述】:
根据今天早些时候在这里提出的question 和许多类似主题的问题,我在这里从 stadard 的角度询问这个问题。
struct Base
{
int member;
};
struct Derived : Base
{
int another_member;
};
int main()
{
Base* p = new Derived[10]; // (1)
p[1].member = 42; // (2)
delete[] p; // (3)
}
按照标准(1)是良构的,因为Dervied*(这是new-expression的结果)可以隐式转换为Base*(C++11草案, §4.10/3):
类型为“pointer to cv D”的纯右值,其中 D 是类类型,可以是 转换为“指向 cv B 的指针”类型的纯右值,其中 B 是基数 D 的类(第 10 条)。如果 B 是不可访问的(第 11 条)或 D 的模棱两可(10.2)基类,一个需要这个的程序 转换是不正确的。转换的结果是一个指向 派生类对象的基类子对象。空指针 value 被转换为目标类型的空指针值。
(3) 由于 §5.3.5/3 导致未定义的行为:
在第一种选择(delete object)中,如果 要删除的对象与其动态类型不同,静态 type 应该是对象的动态类型的基类 已删除且静态类型应具有虚拟析构函数或 行为未定义。在第二种选择(删除数组)中,如果 要删除的对象的动态类型与其静态类型不同, 行为未定义。
(2) 根据标准是否合法,还是会导致程序格式错误或未定义行为?
编辑:更好的措辞
【问题讨论】:
-
为什么我们假设 (2) 格式不正确?
-
(2) 的格式非常错误,因为它使用 sizeof(Base) 来计算 p[0] 和 p[1] 之间的距离。
-
它不是格式错误的,它只是 UB 因为 p 不指向数组对象的元素(指针运算工作的条件),它指向的基类子对象数组元素,因此数组访问无效。
-
@Kerrek SB:也许最后一个问题的措辞应该稍有不同,但由于主要实现(用 gcc、clang 和 MSVC 测试)没有正确,我假设
(2)格式不正确。我花了两个小时搜索类似 Bo Persson 所说的内容,即(p + n)使用p的静态类型来计算偏移量,但我觉得关于operator+的段落并不暗示这一点。 -
@Charles Bailey:哦,这确实有道理。请务必将其发布为答案。