为什么 C++ 需要 public 继承,忽略友元声明,使动态向下转型工作?
Why C++ requires public inheritance, ignoring friend declarations, to make dynamic downcast working?
这里我们有一个classB
,继承自classA
,它有一个friend
classC
.作为朋友,C
应该可以访问 B
中的所有内容,包括 A
基地 class。
测试一下,
- 首先我们创建一个
B
实例。
- 我们将其地址向上转换为
A*
- 然后我们再次尝试用
dynamic_cast<>
向下转换为 B*
。
预期的结果是取回原始B
实例的地址。
#include <cstdint>
#include <cstdio>
class A {
public:
virtual ~A() {};
};
class C;
class B : protected A { // <- this should be public to work! Why?
friend C;
};
class C {
public:
void doit() {
B *b = new B();
printf("b= %p\n", b);
A *a = static_cast<A*>(b);
printf("a= %p\n", a);
B *bb = dynamic_cast<B*>(a);
printf("bb=%p\n", bb);
delete b;
};
};
int main() {
C c;
c.doit();
return 0;
};
解决了类似情况下的常见问题,即基 class 必须是多态的(由其空虚析构函数保证)。
但是,动态转换仍然不起作用:bb
应该与 b
具有相同的地址。
我的实验表明,如果 A
是 B
的 public
基础 class,则唯一的方法就是让它工作。但是... C
是 B
的朋友。 即使 protected
也不起作用。
为什么会这样?
如果重要的话,我使用 gcc-8。
总结评论中有用的信息和引用的资源,可以写一个自答。
首先,friend
声明没有问题:friend
仅影响声明它的 class 成员的可访问性,但不影响其基础 class(es).
其次,C++ 中的可访问性检查在编译时进行。但是 dynamic_cast
的可访问性检查发生在运行时。这种可访问性检查的限制要严格得多,并且 dynamic_cast
只有在继承为 public
.
时才会发生
可能的原因是,正确执行此操作可能需要针对不同访问级别的不同 rtti 表。
这里我们有一个classB
,继承自classA
,它有一个friend
classC
.作为朋友,C
应该可以访问 B
中的所有内容,包括 A
基地 class。
测试一下,
- 首先我们创建一个
B
实例。 - 我们将其地址向上转换为
A*
- 然后我们再次尝试用
dynamic_cast<>
向下转换为B*
。
预期的结果是取回原始B
实例的地址。
#include <cstdint>
#include <cstdio>
class A {
public:
virtual ~A() {};
};
class C;
class B : protected A { // <- this should be public to work! Why?
friend C;
};
class C {
public:
void doit() {
B *b = new B();
printf("b= %p\n", b);
A *a = static_cast<A*>(b);
printf("a= %p\n", a);
B *bb = dynamic_cast<B*>(a);
printf("bb=%p\n", bb);
delete b;
};
};
int main() {
C c;
c.doit();
return 0;
};
解决了类似情况下的常见问题,即基 class 必须是多态的(由其空虚析构函数保证)。
但是,动态转换仍然不起作用:bb
应该与 b
具有相同的地址。
我的实验表明,如果 A
是 B
的 public
基础 class,则唯一的方法就是让它工作。但是... C
是 B
的朋友。 即使 protected
也不起作用。
为什么会这样?
如果重要的话,我使用 gcc-8。
总结评论中有用的信息和引用的资源,可以写一个自答。
首先,friend
声明没有问题:friend
仅影响声明它的 class 成员的可访问性,但不影响其基础 class(es).
其次,C++ 中的可访问性检查在编译时进行。但是 dynamic_cast
的可访问性检查发生在运行时。这种可访问性检查的限制要严格得多,并且 dynamic_cast
只有在继承为 public
.
可能的原因是,正确执行此操作可能需要针对不同访问级别的不同 rtti 表。