11

Let's say I have this code:

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);
}

here class bar has no problems accessing the protected member a from another instance of the same class, but when I try to do the same with an instance of the base class foo, the compiler gives me an error saying that a is protected. What does the standard has to say about this?

error is

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.: I had a look at this question, but it's not quite the same: I'm trying to access the protected member from within the derived class.

Community
  • 1
  • 1
Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55
  • 1
    Same as http://stackoverflow.com/questions/3060572/c-protected-pointer-member-to-the-same-class-and-access-privileges?rq=1 – Eugene K Apr 03 '15 at 21:37
  • Removed the edit as the question is not about the compiler error *per se*, is more about the rationale behind the rule "can't access protected member from base class" – Not a real meerkat Apr 03 '15 at 21:39
  • @EugeneK no. I know I can't access a protected member from the base class. My question is why I can't. – Not a real meerkat Apr 03 '15 at 21:44
  • I think the compiler error is essential in understanding the question, you should put it back in. Or at the very least, put comments on the lines that work and the lines that fail. – Mark Ransom Apr 03 '15 at 21:44
  • Now that you mention it, yeah, that does help. – Not a real meerkat Apr 03 '15 at 21:48
  • Deriving from `foo` does not give `bar` the same access as a `friend` of `foo` so that it can look into any old `foo` object, it only allows it to access to the base parts of a `bar`. – Jonathan Wakely Apr 03 '15 at 21:54

3 Answers3

6

You can access the protected members of a base class only through a pointer or reference to an object of the derived type.

If you change

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

to

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&`
}

you will see the same error.

This SO answer gives an indication of why allowing access to the protected members of the base class will be a potential breach of the protected status of the base class members.

Say you have:

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

and use:

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

If this were allowed, baz will be able to change the values of members of bar. That is not good.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Do you know anything about the rational behind that behavior? – MikeMB Apr 03 '15 at 21:38
  • 1
    I don't know... You are basically transcribing the question as an answer... I'm more interested in the rationale behind why is this like it is. – Not a real meerkat Apr 03 '15 at 21:40
  • 1
    To make sure that *only* derived classes have access to the protected data of a base class. – emvee Apr 03 '15 at 21:47
  • 1
    The rationale seems obvious to me, `protected` means it can be accessed by derived objects, but if you have an arbitrary `foo` there is no way the compiler can know if that `foo` is a base class of some `bar` or just a `foo`. – Jonathan Wakely Apr 03 '15 at 21:48
3

protected means it can be accessed as a member in derived classes. It does not grant derived classes unrestricted access.

The reasoning (I'd presume) is so that the derived class can modify the base type in order to maintain the derived type's own contracts. However, it has no need of accessing the protected members of other derived classes, because that could invalidate their contracts.

A contract is a promise about the states of the members. Some example contracts you might be familiar with are in the internals of string: the size contains the length of the string in the buffer, and buffer[size] contains a null (There's a ton of technicalities here, but they're unimportant). Also that the buffer always points to null or a valid null-terminated string, with unique ownership. The string class works hard to make sure that, no matter what, all of these things are true. (string doesn't actually have any of these contracts because it's members are private, this is just an example)

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
2

This is a common misconception as of what protected means. It does not mean that you can access the member of any object of the base type from a derived type, but only on the subobjects that are part of an object of the derived type.

The rationale is that you get control of the member for your object, where you know what you are doing, but without easily messing up the state of other objects. Consider this example, where CachedDoubleValue maintains a cached value with the double of the value in the base object:

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
}
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489