15

我想知道以下代码是否会导致未定义的行为:

#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);
}
4

2 回答 2

9

向上转换空指针是明确定义的,可以为您提供另一个空指针:

4.10p3:

“pointer to cv D ”类型的纯右值,其中D是类类型,可以转换为“pointer to cv B ”类型的纯右值,其中B是 的基类D。...空指针值转换为目标类型的空指针值。

于 2015-04-24T15:02:31.290 回答
8

Stroustrup 在他 1989 年的多重继承论文 [PDF]的第 4.5 节中讨论了这个案例:

解决方案是详细说明转换(强制转换)操作以测试指针值 0 [...]

增加的复杂性和运行时开销是一个测试和一个增量。

该实现显式检查空值并确保强制转换的结果仍然是空值。这在 C++98 中是正确的,并且在 C++11 和nullptr.

这在多个基类的情况下尤其重要,其中从派生类到基类之一的强制转换可能需要更改指针的实际值。

在您的示例中,内存中的布局C将首先包含字节IA,然后是字节IB。转换到IA是琐碎的,因为指向开头的指针C也将指向IA部分的开头C。另一方面IB,转换为需要将C指针移动IA. 在 nullptr 情况下执行此转换将导致转换后的非空指针,因此对空值进行特殊处理。

正如aschepler 所指出的,标准中的相关部分是 [conv.ptr] §4.10:

“指向cv D的指针”类型的纯右值,其中D是类类型,可以转换为“指向cv B的指针”类型的纯右值,其中B是 的基类 [...] D。[...] 转换的结果是指向派生类对象的基类子对象的指针。空指针值转换为目标类型的空指针值。

于 2015-04-24T15:02:27.157 回答