我想知道以下代码是否会导致未定义的行为:
#include <cstddef>
#include <cstdio>
struct IA {
virtual ~IA() {}
int a = 0;
};
struct IB {
virtual ~IB() {}
int b = 0;
};
struct C: IA, IB {};
int main() {
C* pc = nullptr;
IB* pib = pc;
std::printf("%p %p", (void*)pc, (void*)pib);
}
答案 0 :(得分:9)
向上定义空指针是明确定义的,以便为您提供另一个空指针:
4.10p3:
类型为“指向 cv
D
的指针”的prvalue,其中D
是类类型,可以转换为“指向的指针的prvalue” cvB
“,其中B
是D
的基类。 ...空指针值将转换为目标类型的空指针值。
答案 1 :(得分:8)
Stroustrup在his 1989 multiple inheritance paper [PDF]的第4.5节讨论了这个案例:
解决方案是详细说明转换(强制转换)操作 测试指针值0 [...]
增加的复杂性和运行时开销是一个测试和一个 增量。
实现显式检查空值,并确保转换的结果仍为空值。这在C ++ 98中是正确的,并且在C ++ 11和nullptr
中没有改变。
在多个基类的情况下,这一点尤其重要,其中从派生类到其中一个基类的强制转换可能需要更改指针的实际值。
在您的示例中,内存中C
的布局将首先包含IA
的字节,后跟IB
的字节。转换为IA
是一种琐事,因为指向C
开头的指针也将指向IA
C
部分的开头。另一方面,转换为IB
,需要将C
指针移动IA
的大小。在nullptr情况下执行此移位将导致在强制转换后出现非空指针,因此对空值进行特殊处理。
作为pointed out by aschepler,标准中的相关部分是[conv.ptr]§4.10:
类型为“指向 cv
D
的指针”的prvalue,其中D
是类类型,可以是 转换为“指向 cvB
的指针”的prvalue,其中B
是基数D
的等级[...]。 [...]转换的结果是指向 派生类对象的基类子对象。空指针 value将转换为目标类型的空指针值。