【问题标题】:Cannot access protected member [duplicate]无法访问受保护的成员[重复]
【发布时间】:2011-09-19 19:18:32
【问题描述】:

可能重复:
cannot call base class protected functions?

我不明白以下内容,当Derived 继承自Base 时,它可以访问可以通过派生函数访问的受保护成员。但是,如果Base 类尝试从Derived 类(它本身允许访问Base)访问它自己的成员,它就无法访问,为什么?

class Base {
protected:
    int x;
};

class Derived : Base {
public:
    void foo(Base* b);
};


void Derived::foo(Base* b) {
    b->x = 2;       // cannot access protected member,
                    // though Derived inherits from Base, why?
}

【问题讨论】:

  • 在您的示例中,Derived 没有访问从Base 派生的自己的成员。它正在访问派生自Base 的其他一些对象成员。
  • 另请注意,受保护的数据(相对于方法)非常容易违反基类的不变量。
  • @parc84:我添加了一条评论来解决您删除的问题。

标签: c++ inheritance


【解决方案1】:

一个常见的误解。

Derived::foo() 中,您可以访问Derived 类对象的受保护基成员。但是,*b属于Derived。相反,它是Base 类型,因此它与您的类没有任何关系。

如果您将Derived* 作为参数,那就不同了——那么您确实可以访问受保护的基成员。


让我们把它拼出来:

struct Derived;

struct Base
{
  int f(Derived *);
protected:
  int x;
private:
  int y;
};

struct Derived : public Base
{
  int g(Base *);
  int h(Derived *);
};

int Derived::g(Base * b)
{
   return b->x; // error, protected member of unrelated class
   return b->y; // error, private member of different class
}

int Derived::h(Derived * d)
{
  return d->x;  // OK, protected base member accessible in derived class
  return d->y;  // error, private member of different class
}

int Base::f(Derived * d)
{
  return d->x;  // OK, d converts to Base*
  return d->y;  // OK, ditto
}

【讨论】:

  • 让我们看看。如果没有继承,那么“受保护”将与“私有”相同。所以“b->x”是私有的,Derived::g() 没有访问权限。现在,通过继承,“受保护”意味着Base 子对象 可以在Derived 中访问。但是,b->x 并不引用子对象,因为b 是指向Base 的指针,而不是指向Derived 的指针。 Derived::h() 的情况发生了变化,因为d->x 确实引用了子对象中的x,所以它是可访问的。
  • 这有助于我现在理解,当我想到它时,我想到了为什么b->x 无法通过 main() 或全局访问(如果它受到保护)的相同原因。
【解决方案2】:

你碰到了标准中的一条特殊规则:

11.5 受保护的会员访问权限
当派生类的友元或成员函数引用基类的受保护的非静态成员函数或受保护的非静态数据成员时,除了前面第 11 节中描述的那些之外,还应用访问检查。除非形成指向成员的指针,*必须通过派生类本身(或从该类派生的任何类)的指针、引用或对象进行访问。

这种附加访问检查的一个原因是与这些基类受保护成员的行为有关。由于成员受到保护,不同的派生类可以为这些派生成员的含义添加语义,甚至可以对其进行大规模更改。 (这是受保护数据相当危险的原因之一。)因为您的类不知道在其他派生类中对基类语义所做的这些添加/修改,所以最好的办法是在以下情况下排除对基类成员的访问访问将通过基类进行。

【讨论】:

    【解决方案3】:

    提供其他人所说的具体例子:

    class Base {
    protected:
      int x;
    };
    
    class Derived : Base {
    public:
      void foo(Derived*, Base*);
    };
    
    int main() {
      Base fiddle;
      Derived fast, furious;
      fast.foo(&furious, &fiddle);
    }
    
    void Derived::foo(Derived *d, Base* b) {
      x = 1;       // Legal, updates fast.x
      this->x = 2; // Legal, updates fast.x
      d->x = 3;    // Legal, updates furious.x
      b->x = 4;    // Error, would have updated fiddle.x
    }
    

    【讨论】:

      【解决方案4】:

      你有正确的想法,但你没有正确使用受保护的成员。

      void foo(Base* b) 应该改为 void foo();

      它的实现是:

      无效派生::foo() { 返回这个-> x; }

      因为 x 是受保护的成员,所以您不能从另一个对象访问它——即使该对象继承自该类。您只能从派生对象本身访问它。

      【讨论】:

      • 您的示例与 OP 的示例不同,在这种情况下具有误导性。
      • 不完全正确。访问限制是每个类,而不是每个实例。
      • 除非他写了他想要的,并且 foo 需要一个指向另一个 Base* 对象的指针。
      • @Kerrek:我认为他的意思是Derived1 : Base 不能访问Derived2 : Basex 成员。
      • @Moo:他说“来自派生对象”;这是不正确的。
      【解决方案5】:

      您实际上所做的是创建了一个 base 实例,该实例与derived 的关系与派生的base 内部实例的关系不同。将变量设置为 protected 可以让继承的类访问它自己的内部基实例。但是,在类中创建 base 类型的对象是不同的,因此不允许访问。

      【讨论】:

        猜你喜欢
        • 2015-01-30
        • 2012-11-20
        • 2011-01-02
        • 2016-01-07
        • 2015-11-29
        • 2023-03-30
        • 1970-01-01
        • 2013-11-30
        • 2014-01-26
        相关资源
        最近更新 更多