私有范围内私有继承的动态下转

时间:2011-08-03 14:13:37

标签: c++ casting private downcast

对我遇到的this question进行了调整。考虑:

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实际上属于B*类型,并且因为我们可以完全访问B及其A的继承,所以我预计两个演员都工作。但他们没有;为什么?还有另一种方法来实现这种演员吗?

请注意:

  • 如果foo()不是B的成员,则两个演员都会失败。
  • 如果B公开继承A,则两个演员都会有效。

2 个答案:

答案 0 :(得分:15)

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

  

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

     

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

(回想11.2 “如果可以访问基类的发明公共成员,则可以访问基类。”)。

这解释了为什么第一个演员会工作。现在,第二个:

  

[...]

     

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

     

运行时检查按逻辑执行如下:

     
      
  • 如果,在v指向(引用)的最派生对象中,v点   (指)public对象的T基类子对象,如果只是   类型T的一个对象是从指向的子对象派生的(引用)   通过v,结果是指向T的指针(左值引用)   对象。
  •   
  • 否则,如果v指向(引用)public基类子对象   最派生对象的类型,以及派生最多对象的类型   有一个类型为T的基类,它是明确的public ,   result是指向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资格,或更高的cv资格,和   B不是D的虚基类。空指针值(4.10)被转换为空指针值   目的地类型。如果“指向cv1 B的指针”类型的rvalue指向实际上是b的子对象的B.   D类型的对象,结果指针指向类型为D的封闭对象。否则,结果为   演员未定义。

因此,如果您确定指针的实际动态类型是什么,则允许static_cast内的foo

我对为何存在此不一致的任何其他信息感兴趣。

答案 1 :(得分:1)

它们不起作用,因为A中没有虚函数。当你进行向下转换然后那是微不足道的时候 - 编译器可能甚至不打算进行检查。当你进行向上转换时,编译器必须检查,但它只定义为在你有虚函数时工作。如果不这样做,则编译器将无法进行检查,结果为NULL

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

  

请注意:

     

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

     

如果B继承   来自A公开场合,两位演员都会工作。

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