在今天早些时候提出的问题以及大量相似的主题问题之后,我在这里从标准的角度询问这个问题.
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)
}
Run Code Online (Sandbox Code Playgroud)
根据标准(1)
是格式良好的,因为Dervied*
(这是new-expression的结果)可以隐式转换为Base*
(C++ 11 draft,§4.10/ 3):
类型为"指向cv D的指针"的prvalue ,其中D是类类型,可以转换为类型为"指向cv B的指针"的prvalue ,其中B是D的基类(子句10).如果B是D不可访问(第11条)或模糊(10.2)基类,需要这种转换的程序是不正确的.转换的结果是指向派生类对象的基类子对象的指针.空指针值将转换为目标类型的空指针值.
(3)
由于§5.3.5/ 3导致未定义的行为:
在第一个备选(删除对象)中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类,静态类型应具有虚拟析构函数或行为未定义.在第二个备选(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义.
(2)
根据标准是合法的还是会导致形成不良的程序或不明确的行为?
编辑:更好的措辞
如果您查看表达式p[1]
, p
is a Base*
(Base
是完全定义的类型)和1
is an int
,那么根据 ISO/IEC 14882:2003 5.2.1 [expr.sub] 该表达式是有效的并且与 相同*((p)+(1))
。
从5.7 [expr.add] / 5开始,当一个整数与一个指针相加时,只有当指针指向数组对象的一个元素并且指针算术的结果也指向该数组对象的一个元素时,结果才是明确定义的数组对象或数组末尾的一个对象。p
但是,并不指向数组对象的元素,它指向对象的基类子对象Derived
。作为Derived
数组成员的是对象,而不是Base
子对象。
请注意,在 5.7 / 4 下,出于加法运算符的目的,Base
子对象可以被视为大小为 1 的数组,因此从技术上讲,您可以形成地址p + 1
,但作为“最后一个元素过去一个”指针,它不指向Base
对象,尝试读取或写入该对象将导致未定义的行为。
归档时间: |
|
查看次数: |
2866 次 |
最近记录: |