Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

subtle C++ inheritance error with protected fields

Below is a subtle example of accessing an instance's protected field x. B is a subclass of A so any variable of type B is also of type A. Why can B::foo() access b's x field, but not a's x field?

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

Here is the error I get with 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 
like image 352
wcochran Avatar asked Aug 08 '11 18:08

wcochran


1 Answers

Since B is publicly inherited from A, A's protected member(s) become B's protected member(s), so B can access its protected members as usual from its member function(s). That is, the objects of B can access the protected members of B from its member functions.

But A's protected members cannot be accessed outside the class, using object of type A.

Here is the relevant text from the Standard (2003)

11.5 Protected member access [class.protected]

When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11.102) Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).

And the example follows from the Standard (2003) itself as:

[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] 

Note in the above example fr() is a friend function of D2, mem() is a member function of D2, and g() is neither a friend, nor a member function.

like image 157
Nawaz Avatar answered Oct 14 '22 16:10

Nawaz