C++嵌套结构体继承规则(访问受保护的成员)

10

有人可以解释一下为什么(也就是说,“语言为什么会这样?”),以下代码在B::C::bar的第二行会编译错误吗?

class A
{
public:
    struct D
    {
        void call_foo (A &a)
        {
            a.foo ();
        }
    };

protected:
    void foo () {}
};

class B : public A
{
    struct C : public A::D
    {
        void bar (A &a, B &b)
        {
            b.foo (); // OK
            a.foo (); // Error. Huh?
            call_foo (a); // Ugly workaround
        }
    };
};

如果且仅当基础指针的类型正好是封闭类型(而不是某个父类型)时,似乎可以安全地在父类中使用受保护的方法。

这似乎有点奇怪。为什么语言会这样设计呢?


想象一下,A&来自完全不同的类层次结构,与B毫无关系,如果从C中访问它,将会破坏封装的理念。 - Oleg Bogdanov
1
这对我来说相当神秘。例如,请检查:https://dev59.com/l3M_5IYBdhLWcg3wWRuV#1414520,其中引用了Bjarne Stroustrup的话,他说“派生类只能为自己类型的对象访问基类的受保护成员... 这可以防止一个派生类损坏属于其他派生类的数据时可能出现的微妙错误。” 但这仍然不太有帮助,不是吗? - Tarc
我提出的这个问题基本上是与上面评论中的问题重复了。而且,我认为单一的答案并没有真正回答它。请有所需超能力的人将其关闭为重复项? - Rupert Swarbrick
1个回答

8
结构体C嵌套在类B中,被视为成员,因此具有与任何其他成员相同的访问权限。所以它可以访问基类Aprotected成员。但请注意,您只能通过B类型的对象访问Aprotected成员;您不能通过A访问。这是有道理的,因为派生类的成员应该只能访问从基类继承的protected成员;这些成员属于派生类。但是直接访问基类的protected成员是不允许的;它们属于基类(或其他派生类)。
这个规则对于内部类并不特殊,对于B的成员函数也是如此。 §11.4/1 Protected member access [class.protected] (强调我的)

An additional access check beyond those described earlier in Clause [class.access] is applied when a non-static data member or non-static member function is a protected member of its naming class ([class.access.base])115 As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member ([expr.unary.op]), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve a (possibly implicit) object expression ([expr.ref]). In this case, the class of the object expression shall be C or a class derived from C. [ Example:

class B {
protected:
  int i;
  static int j;
};

class D1 : public B {
};

class D2 : public B {
  friend void fr(B*,D1*,D2*);
  void mem(B*,D1*);
};

...

void D2::mem(B* pb, D1* p1) {
  pb->i = 1;                    // ill-formed
  p1->i = 2;                    // ill-formed
  i = 3;                        // OK (access through this)
  B::i = 4;                     // OK (access through this, qualification ignored)
  int B::* pmi_B = &B::i;       // ill-formed
  int B::* pmi_B2 = &D2::i;     // OK
  j = 5;                        // OK (because j refers to static member)
  B::j = 6;                     // OK (because B​::​j refers to static member)
}

...

 — end example ]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接