为什么 C++ 需要 public 继承,忽略友元声明,使动态向下转型工作?

Why C++ requires public inheritance, ignoring friend declarations, to make dynamic downcast working?

这里我们有一个classB,继承自classA,它有一个friendclassC.作为朋友,C 应该可以访问 B 中的所有内容,包括 A 基地 class。

测试一下,

  1. 首先我们创建一个 B 实例。
  2. 我们将其地址向上转换为 A*
  3. 然后我们再次尝试用 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 具有相同的地址。

我的实验表明,如果 ABpublic 基础 class,则唯一的方法就是让它工作。但是... CB 的朋友。 即使 protected 也不起作用。

为什么会这样?

如果重要的话,我使用 gcc-8。

总结评论中有用的信息和引用的资源,可以写一个自答。

首先,friend 声明没有问题:friend 仅影响声明它的 class 成员的可访问性,但不影响其基础 class(es).

其次,C++ 中的可访问性检查在编译时进行。但是 dynamic_cast 的可访问性检查发生在运行时。这种可访问性检查的限制要严格得多,并且 dynamic_cast 只有在继承为 public.

时才会发生

可能的原因是,正确执行此操作可能需要针对不同访问级别的不同 rtti 表。