为什么我无法从基类的实例访问受保护的成员?

Why can't I access a protected member from an instance of a base class?

本文关键字:实例 访问 成员 受保护 基类 为什么      更新时间:2023-10-16

假设我有这样的代码:

class foo{
  protected:
  int a;
};
class bar : public foo {
  public:
  void copy_a_from_foo(foo& o){
    a = o.a; // Error
  }
  void copy_a_from_bar(bar& o){
    a = o.a; // OK
  }
};
int main(){
  bar x;
  foo y;
  bar z;
  x.copy_a_from_foo(y);
  x.copy_a_from_bar(z);
}

这里class bar从同一类的另一个实例访问受保护成员a没有问题,但当我尝试对基类foo的实例执行同样的操作时,编译器会给我一个错误,说a受到保护。标准对此有何规定?

错误为

prog.cpp: In member function 'void bar::copy_a_from_foo(foo&)':
prog.cpp:3:7: error: 'int foo::a' is protected
   int a;
       ^
prog.cpp:9:11: error: within this context
     a = o.a;

附言:我看了这个问题,但不太一样:我试图从派生类中访问受保护的成员。

只能通过指向派生类型对象的指针或引用来访问基类的protected成员。

如果您更改

void copy_a_from_bar(bar& o){
  a = o.a;
}

void copy_a_from_bar(bar& o){
  foo& foo_ref = o;
  a = o.a;        // OK. Accessing it through `bar&`
  a = foo_ref.a;  // Not OK. Accessing it through `foo&`
}

你会看到同样的错误。

这个SO回答给出了为什么允许访问基类的protected成员将是对基类成员的protected状态的潜在破坏的指示。

假设你有:

class baz : public foo
{
   void update_a(foo& f)
   {
      f.a = 20;
   }
};

和用途:

bar x;
baz z;
z.update_a(x);

如果允许,baz将能够更改bar的成员的值。这不好。

protected意味着它可以作为派生类中的成员访问。它不会授予派生类不受限制的访问权限。

推理(我想)是这样的,派生类可以修改基类型,以便维护派生类型自己的约定。但是,它不需要访问其他派生类的受保护成员,因为这可能会使合约无效。

合同是对成员国状态的承诺。您可能熟悉的一些示例契约位于字符串的内部:size包含缓冲区中字符串的长度,buffer[size]包含null(这里有很多技术细节,但它们并不重要)。此外,缓冲区总是指向null或以null结尾的有效字符串,具有唯一所有权。字符串类努力确保,无论怎样,所有这些都是真的。(字符串实际上没有任何这些契约,因为它的成员是私有的,这只是一个例子)

对于protected的含义,这是一个常见的误解。这并不意味着您可以从派生类型访问任何基类型对象的成员,而只能访问作为派生类型对象一部分的子对象。

其原理是,您可以控制对象的成员,在那里您知道自己在做什么,但不会轻易扰乱其他对象的状态。考虑这个例子,其中CachedDoubleValue使用基础对象中值的两倍来维护缓存值:

class Base {
protected:
   int x;
};
class CachedDoubleValue : public Base {
   int y;
public:
   void set(int value) {
      x = value;
      y = 2 * x;
   }
};
class AnotherDerived : public Base {
public:
    static void setX(Base &b, int value) {
       b.x = 10;
    }
};
int main() {
    CachedDoubleValue a;
    a.set(1);             // a.x = 1, a.y = 2
    AnotherDerived::modifyX(a, 10);
        // Invariant broken: a.x = 10, a.y = 2; a.x != 2 * a.y
}