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

Posted

技术标签:

【中文标题】带有受保护字段的微妙 C++ 继承错误【英文标题】:subtle C++ inheritance error with protected fields 【发布时间】:2011-10-22 15:24:38 【问题描述】:

下面是访问实例的受保护字段 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

【问题讨论】:

【参考方案1】:

由于B 是从A 公开继承的,A 的受保护成员成为 B 的受保护成员,因此 B 可以像往常一样从其成员函数访问其受保护成员。即B的对象可以通过其成员函数访问B的受保护成员。

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

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

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

当派生类的友元或成员函数引用基类的受保护的非静态成员函数或受保护的非静态数据成员时,除了前面第 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()既不是友元,也不是成员函数。

【讨论】:

因为是公有继承,A的受保护成员变成B的受保护成员。不是私人的。 @Billy:正确。并更正了答案。 Public Ihneritance中,基类的受保护成员成为派生类的受保护成员,基类的公共成员成为派生类的公共成员。 但我正在“通过指向、引用或派生类本身(或从该类派生的任何类)的对象的指针、引用或对象”访问。 @David:在这种情况下,派生类会显式地进行调用。我看不出受保护的继承与我们所说的有什么关系。 Nawaz 只是在谈论当公共继承发挥作用时会发生什么。 (此外,我不了解你;我从未见过教科书之外的受保护继承)【参考方案2】:

考虑:

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.
  
;

【讨论】:

这并不能解释,因为如果afoo 都是A 的成员(即a 可能是@987654327 的一个实例),您将有相同的论点@) -- 但这用 C++ 编译得很好! @wcochran:不正确。在这个例子中,B 根本不在C 的继承链中——它不应该访问C 的任何部分。如果两者都是A 的成员,那么A 将在两个类的继承链。 如果我将A *a; 字段添加到A(暂时忘记'B'),那么a 可能是一个指向输入C——同样的问题,编译正常。 @wchchran:不,它没有。 ideone.com/29pyK如果不是你的意思,贴个例子,我会解释的。 @wcochran:不,这不是一般的 OOP 规则。一般的 OOP 规则是派生类继承其基类的行为。允许这种访问将继承 C 的行为,即使 C 不在 B 的继承层次结构中(因此格式不正确)【参考方案3】:

公有继承中:基类的所有Public members变成派生类&的Public Members 基类的所有Protected members变成Derived ClassProtected Members

按照上述规则: 来自A 的受保护成员x 成为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;

【讨论】:

x在本例中是B的受保护成员,不允许访问。我认为这不能回答问题。 @Billy ONeal:在 C++ 中,访问控制是基于每个类而不是基于每个对象的,因此 B 类的对象可以访问 B 类的另一个对象的成员。【参考方案4】:

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

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

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

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:

让我们从基本概念开始,

class A 
protected:
   int x;
;

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

由于孩子继承父母,孩子得到x。因此,您可以直接在 child 的 foo() 方法中访问 x。这就是受保护变量的概念。您可以直接访问子级中父级的受保护变量。 注意:这里我是说你可以直接访问 x,但不能通过 A 的对象!有什么不同 ?由于 x 受到保护,因此您无法在 A 之外访问 A 的受保护对象。不管它在哪里 - 如果它是 main 或 Child 。这就是为什么您无法通过以下方式访问的原因

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 是纯面向对象的:)

【讨论】:

以上是关于带有受保护字段的微妙 C++ 继承错误的主要内容,如果未能解决你的问题,请参考以下文章

继承受保护函数和公共变量 C++ 的多重继承编译错误

eclipse C++ 中无法识别受保护的成员

C# & C++:“试图读取或写入受保护的内存”错误

c# 访问 c++ dll 时尝试读取或写入受保护的内存错误

C++ 为啥要使用公有、私有或受保护的继承?

你能在 C++ 中保护嵌套类吗?