受保护字段的细微C++继承错误

subtle C++ inheritance error with protected fields

本文关键字:C++ 继承 错误 字段 受保护      更新时间:2023-10-16

下面是访问实例的受保护字段 x 的简单示例。B 是 A 的子类,因此 B 类型的任何变量也是 A 类型。为什么 B::foo(( 可以访问 b 的 x 字段,而不能访问 a 的 x 字段?

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++ 时遇到的错误

$ 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 的受保护成员,因此 B 可以像往常一样从其成员函数访问其受保护成员。也就是说,B的对象可以从其成员函数访问B的受保护成员。

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

以下是标准(2003(的相关文本

11.5 受保护的成员访问 [类受保护]

当派生类的友元或成员函数引用基类的受保护的非静态成员函数或受保护的非静态数据成员时,除了前面的条款 11.102 中所述的内容外,还适用访问检查(除非形成指向成员的指针 (5.3.1(,否则访问必须通过指向派生类本身(或从该类派生的任何类(的指针、引用或对象 (5.2.5(。如果访问要形成指向成员的指针,则嵌套名称说明符应命名派生类(或任何 类派生自该类(。

该示例来自标准(2003(本身:

[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()D2的成员函数,g()既不是友元函数,也不是成员函数。

考虑:

class A {
protected:
  int x;
};
class C : public A
{
};
class B : public A {
protected:
  unique_ptr<A> a;
public:
  B() : a(new C) // a now points to an instance of "C"
  { }
  void foo() {
    int w = a->x;  // B accessing a protected member of a C? Oops.
  }
};

公共继承中:
基类的所有Public members都成为派生类
Public Members基类的所有Protected members都成为Derived ClassProtected Members

根据上述规则:
受保护成员xA成为类 B 的受保护成员。

class B可以在其成员函数foo中访问自己的受保护成员,但它只能访问派生它的A的成员,而不是所有类A

在这种情况下class B包含一个A指针a,它无法访问这个包含的类的受保护成员。

为什么B::foo()可以访问包含的class B指针b的成员?

规则是:
在C++访问控制基于每个类而不是基于每个对象工作。
因此,class B的实例将始终可以访问另一个class B实例的所有成员。

演示规则的代码示例:

#include<iostream>
class MyClass 
{
    public: 
       MyClass (const std::string& data) : mData(data) 
       {
       }
       const std::string& getData(const MyClass &instance) const 
       {
          return instance.mData;
       }
    private:
      std::string mData;
};
int main() {
  MyClass a("Stack");
  MyClass b("Overflow");
  std::cout << "b via a = " << a.getData(b) << std::endl;
  return 0;
}

为什么 B::foo(( 可以访问 b 的 x 字段,而不能访问 a 的 x 字段?

受保护的成员只能由同一类(或派生类(的其他成员访问。

b->x指向类 B 实例的受保护成员(通过继承(,因此B::foo()可以访问它。

a->x指向类 A 实例的受保护成员,因此B::foo()无法访问它。

B与类A不同。这就是为什么类 B 的成员无法访问类 A 的非公共成员。

另一方面,类B公开派生自类A,所以类B现在有一个(受保护的(成员x,任何类B的成员都可以访问。

让我们从基本概念开始,

class A {
protected:
   int x;
};
class B : public A {
public:
  void foo() {
    int u = x;     // OK : accessing inherited protected field
  }
};

由于 child 继承了父项,因此 child 得到 x.因此,您可以直接在 child 的 foo(( 方法中访问 x。这就是受保护变量的概念。您可以直接访问子级中父级的受保护变量。注意:这里我说的是你可以直接访问x,但不能通过A的对象!有什么区别?由于 x 是受保护的,因此您无法在 A 之外访问 A 的受保护对象。这就是为什么您无法通过以下方式访问的原因

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

这里有一个有趣的概念。您可以使用类中的对象访问类的私有变量!

class dummy {
private : 
int x;
public:
  void foo() {
    dummy *d;
    int y = d->x; // Even though x is private, still you can access x from object of d - But only with in this class. You cannot do the same outside the class. 
  }
};

受保护的变量也是如此。因此,您可以访问以下示例。

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

希望它能解释:)

C++是完整的面向对象编程,而Java是纯面向对象的:)