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

时间:2015-04-03 21:26:34

标签: c++

假设我有这段代码:

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;

P.S。:我查看了this question,但它并不完全相同:我正在尝试从派生类中访问受保护的成员。

3 个答案:

答案 0 :(得分:6)

只能通过指向或派生类型对象的引用来访问基类的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&`
}

你会看到同样的错误。

This SO answer说明为什么允许访问基类的protected成员可能会违反基类成员的protected状态。

说你有:

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

并使用:

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

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

答案 1 :(得分:3)

protected表示可以在派生类中将作为成员进行访问。 授予派生类无限制访问权。

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

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

答案 2 :(得分:2)

这是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
}