带有受保护字段的微妙 C++ 继承错误

2021-12-17 00:00:00 inheritance protected c++

下面是一个访问实例的受保护字段 x 的微妙示例.B 是 A 的子类,所以任何 B 类型的变量也是 A 类型.为什么 B::foo() 可以访问 b 的 x 字段,而不能访问 a 的 x 字段?

Below is a subtle example of accessing an instance's protected field x. B is a subclass of A so any variable of type B is also of type A. Why can B::foo() access b's x field, but not a's x field?

class A {
protected:
  int x;
};

class B : public A {
protected:
  A *a;
  B *b;
public:
  void foo() {
    int u = x;     // OK : accessing inherited protected field x
    int v = b->x;  // OK : accessing b's protected field x
    int w = a->x;  // ERROR : accessing a's protected field x
  }
};

这是我用 g++ 得到的错误

Here is the error I get with g++

$ g++ -c A.cpp
A.cpp: In member function ‘void B::foo()’:
A.cpp:3: error: ‘int A::x’ is protected
A.cpp:14: error: within this context

推荐答案

由于 B 公开继承自 A,A 的受保护成员成为 B 的受保护成员(s),因此 B 可以像往常一样从其成员函数访问其受保护的成员.也就是说,B 的对象可以从其成员函数访问B 的受保护成员.

Since B is publicly inherited from A, A's protected member(s) become B's protected member(s), so B can access its protected members as usual from its member function(s). That is, the objects of B can access the protected members of B from its member functions.

但是 A 的受保护成员不能在类外访问,使用 A 类型的对象.

But A's protected members cannot be accessed outside the class, using object of type A.

这是标准 (2003) 中的相关文本

Here is the relevant text from the Standard (2003)

11.5 受保护的成员访问 [class.protected]

当派生类的友元或成员函数引用基类的受保护的非静态成员函数或受保护的非静态数据成员时,除了前面第 11.102 节中描述的那些之外,还应用访问检查)除非形成指向成员(5.3.1),访问必须通过派生类本身(或从该类派生的任何类)的指针、引用或对象(5.2.5).如果访问是形成一个指向成员的指针,则嵌套名称说明符应命名派生类(或任何从该类派生的类).

When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11.102) Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).

这个例子来自标准 (2003) 本身:

And the example follows from the Standard (2003) itself as:

[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 fr(B* pb, D1* p1, D2* p2)
{
  pb->i = 1; // ill-formed
  p1->i = 2; // ill-formed
  p2->i = 3; // OK (access through a D2)
  p2->B::i = 4; // OK (access through a D2, even though naming class is B)
  int B::* pmi_B = &B::i; // ill-formed
  int B::* pmi_B2 = &D2::i; // OK (type of &D2::i is int B::*)
  B::j = 5; // OK (because refers to static member)
  D2::j =6; // OK (because refers to static member)
}
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)
}
void g(B* pb, D1* p1, D2* p2)
{
  pb->i = 1; // ill-formed
  p1->i = 2; // ill-formed
  p2->i = 3; // ill-formed
}
―end example]

注意上面例子中fr()D2的友元函数,mem()的成员函数D2g() 既不是友元函数,也不是成员函数.

Note in the above example fr() is a friend function of D2, mem() is a member function of D2, and g() is neither a friend, nor a member function.

相关文章