【问题标题】:How does friend class of the base class access members of that base class through objects of class derived from the base class?基类的友元类如何通过派生自基类的类对象访问该基类的成员?
【发布时间】:2023-03-03 02:16:01
【问题描述】:

这是我在 source.cpp 文件中的代码:

class B
{
  friend class F;
protected:
  int protectedIntB;
};

class D : public B {};

class F
{
public:
  int f(D &d) {return ++d.protectedIntB;}
};

当我用g++ -c -Wall -pedantic -std=c++11 source.cppcl /c source.cpp 编译上面的代码时,两个编译器都编译成功。但是,当我使用 protected 而不是 public 从 B 继承 D 时:

class D : protected B {};

这一次,gcc 编译成功,而 cl 报错说 B::protectedIntB is inaccessible in return ++d.protectedIntB;.

另一种情况是用private替换public

class D : private B {};

这一次,两个编译器都会产生错误。顺便说一句,我使用的是由 mingw-w64 构建的 gcc 版本 5.3.0 和 VS2015 的 cl 版本 19.00.24210。

我的问题来了:

基类的友元类如何通过基类派生类的对象访问该基类的成员,为什么gcc和cl处理方式不同?

编辑:

感谢songyuanyaoBrian,在protected 的情况下,这似乎是gcc 5.3.0 中的一个错误。只有public的情况应该编译成功,gcc 6.1.0也可以正常工作。

【问题讨论】:

    标签: c++ inheritance language-lawyer friend


    【解决方案1】:

    如果代码在 gcc 5.3.0 上编译并且不在 cl 上编译,则很可能两者之一没有严格执行 C++ 标准。如果我不得不猜测,对于受保护的和私有的继承,你应该得到一个编译器错误。有关不同类型继承之间的区别,请参阅Difference between private, public, and protected inheritance 了解更多详细信息。

    为了让类 F 能够访问类 B 的私有成员,它应该知道类 D 派生自类 B,这只会发生在公共继承中。

    【讨论】:

      【解决方案2】:

      根据[class.access.base]/5:

      对成员的访问受到影响 由成员命名的类。此命名类是成员名称所在的类 查了一下,发现了。

      根据[class.access.base]/6:

      如果使用类成员访问运算符(包括隐式“this->”)访问非静态数据成员 或非静态成员函数,如果左操作数(被视为 “.”运算符情况)不能隐式转换为指向右操作数命名类的指针。

      因此,在您的特定情况下,要访问d.protectedIntB,以下两项都必须为真:

      • 作为B 的成员,您必须有权访问protectedIntB,因为B 是在其中找到名称protectedIntB 的类。 (注意:这可以通过使用 using-declaration 重新声明派生类中的成员来改变; 在这种情况下,派生类将控制。)

      • 您必须有权访问B 作为D 的基础,能够将D* 转换为B*。如果BD 的公共基础,那很好。如果BD 的受保护基,则应用访问检查,F::f 失败,因为F 不是D 的朋友,也不是D 的派生类。

      令人惊讶的是,GCC 似乎是受保护情况下错误的编译器,但这个错误 appears fixed。请注意,Clang 给出了 much better diagnostic

      【讨论】:

        【解决方案3】:

        如果您使用 protected 或 private 而不是 public 使 DB 继承,编译应该会失败。

        来自标准,$11.2/5 Accessibility of base classes and base class members [class.access.base]

        当在类 N 中命名时,成员 m 可以在点 R 访问

        (5.4) 存在可在 R 访问的 N 的基类 B,并且 当在 B 类中命名时,m 可在 R 处访问。 [示例:

        class B;
        class A {
        private:
          int i;
          friend void f(B*);
        };
        class B : public A { };
        void f(B* p) {
          p->i = 1;         // OK: B* can be implicitly converted to A*,
                            // and f has access to i in A
        }
        

        ——结束示例]

        对于您的第一种情况,D 的基类 B 可通过 F::f() 访问,因为它是公共继承。而B::protectedIntB 可以通过F::f() 访问,因为它是B 班级的朋友。

        如果将其更改为受保护或私有继承,D 的基类B 将无法在F::f() 再次访问,那么编译应该会失败。注意F::f() 不是派生类D 的朋友。这意味着如果你也让它成为 D 类的朋友,编译就会成功。

        顺便说一句:我用 gcc here 尝试了受保护的继承,但它失败了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-05
          • 1970-01-01
          • 1970-01-01
          • 2017-01-16
          • 2017-09-30
          • 1970-01-01
          • 2022-01-19
          • 2011-04-21
          相关资源
          最近更新 更多