对我遇到的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
,则两个演员都会有效。答案 0 :(得分:15)
5.2.7(ISO / IEC 14882,12 / 29/2003)在这一点上非常明确:
[关于表达式
dynamic_cast<T>(v)
]如果
T
是“指向 cv1B
的指针”并且v
具有“指向 cv2的指针D
“B
是D
的基类,结果是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仍然没有虚函数,它仍然无效。