19

我遇到的这个问题的调整。考虑:

class A {};

class B : private A {
   static void foo();
};

void B::foo(){
   B* bPtr1 = new B;
   A* aPtr1 = dynamic_cast<A*>(bPtr1); // gives pointer
   B* bPtr2 = dynamic_cast<B*>(aPtr1); // gives NULL
}

aPtr1实际上,因为is 是 type B*,并且我们可以完全访问B并从 继承它A,所以我希望这两种强制转换都能正常工作。但他们没有;为什么?有没有另一种方法来实现这个演员?

注意:

  • 如果foo()不是 B 的成员,则两个强制转换都会失败。
  • 如果从公开B继承,这两种演员都可以工作。A
4

2 回答 2

16

5.2.7 (ISO/IEC 14882, 12/29/2003) 在这一点上非常明确:

[关于表达dynamic_cast<T>(v)]

如果T是“指向cv1 B的指针”并且v具有类型“指向cv2 D的指针”,它B是 的基类D,则结果是指向 指向的对象的唯一子B对象的指针。[... bla bla about cv1 和 cv2 ...]和 B 应该是 D 的可访问的明确基类(强调我的)Dv

(回忆 11.2 “如果基类的一个发明的公共成员是可访问的,则称该基类是可访问的。”)。

这就解释了为什么第一个演员表有效。现在,对于第二个:

[...]

否则,将应用运行时检查以查看是否v可以将指向或引用的对象转换为指向或引用的类型T

运行时检查在逻辑上执行如下:

  • 如果在由 指向(引用)的最派生对象中vv指向(引用)一个对象的public基类子T对象,并且如果只有一个 T 类型的对象从由 指向(引用)的子对象派生v,则result 是指向该T 对象的指针(左值引用)。
  • 否则,如果v指向(引用)最派生对象的public基类子对象,并且最派生对象的类型具有类型为 的基类,该基类T明确的并且public,则结果是一个指针(一个左值引用) 到T最派生对象的子对象。
  • 否则,运行时检查将失败。

转换为指针类型失败的值是所需结果类型的空指针值。转换为引用类型失败会抛出 bad_cast (18.5.2)。

因此,您观察到的行为似乎是由于private继承:即使基类是可访问的,它也不是public,并且标准要求public,不可访问。

烦人,不是吗?我手边没有 C++0x 草稿,也许有人可以用它的引号来编辑我的答案,以防万一发生了变化。

有没有另一种方法来实现这个演员?

这取决于你想做什么。基本上,私有继承只是执行组合的另一种设备。如果你真的要返回一个指向私有派生实例的指针,那么要么将继承公开,要么返回一个成员。

无论如何,你会很高兴知道这static_cast似乎没有这个限制:

5.2.9。[关于static_cast<T>(v)] [...]

“指向 cv1 B 的指针”类型的右值,其中 B 是类类型,可以转换为“指向 cv2 D 的指针”类型的右值,其中 D 是从 B 派生的类(第 10 条),如果一个有效的标准存在从“指向 D 的指针”到“指向 B 的指针”的转换(4.10),cv2 与 cv1 具有相同的 cv 限定或大于 cv1 的 cv 限定,并且 B 不是 D 的虚拟基类。空指针value (4.10) 被转换为目标类型的空指针值。如果“指向 cv1 B 的指针”类型的右值指向实际上是 D 类型对象的子对象的 B,则生成的指针指向 D 类型的封闭对象。否则,强制转换的结果是未定义的.

所以如果你确定指针的实际动态类型是什么,你就可以进入static_castinside foo

我会对有关为什么存在这种不一致的任何其他信息感兴趣。

于 2011-08-03T14:26:26.210 回答
1

它们不起作用,因为 A 中没有虚函数。当您进行向下转换时,这很简单——编译器甚至可能不会费心进行检查。当您进行向上转换时,编译器必须进行检查,但它仅在您拥有虚函数时才被定义为工作。如果不这样做,编译器将无法进行检查,结果为NULL.

继承保护级别和其他可访问性问题与问题正交,它们仅存在于编译时,如果程序编译则它们可以正常工作。

注意:

如果 foo() 不是 B 的成员,则两个强制转换都会失败。

如果 B 公开地从 A 继承,则两种转换都可以工作。

那不是真的。foo()与 RTTI 功能完全无关——它不是虚拟的,甚至不是实例成员。如果 B 从 A 公开继承 - 那么 A 仍然没有虚函数,它仍然无法工作。

于 2011-08-03T14:27:43.103 回答