tags:

views:

412

answers:

4

Hi,

I am confused: I thought protected data was read/writable by the children of a given class in C++.

The below snippet fails to compile in MS Compiler

class A
{
protected:
  int data;
};

class B : public A
{
  public:

  B(A &a)
  {
    data = a.data;
  }
};

int main()
{
  A a;
  B b = a;
  return 0;
}

Error Message:

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

demoFail.cpp
demoFail.cpp(12) : error C2248: 'A::data' : cannot access protected member declared in class 'A'
        demoFail.cpp(4) : see declaration of 'A::data'
        demoFail.cpp(2) : see declaration of 'A'

What am I doing wrong?

A: 

The constructor of B is private. If you do not specify anything, in a class, the default modifier is private (for structs it is public). So in this example the problem is that you cannot construct B. When you add public to constructor B anoter problem arises:

B has the right to modify the part of A it derives from but not another A like in this case.

You could do following:

class A
{
public:
  A()
      : data(0)
  {
  }
  A(A &a)
  {
    data = a.data;
  }
protected:
  int data;
};

class B : public A
{
public:
  B(A &a)
      : A(a)
  {
  }
};

int main()
{
  A a;
  B b = a;
  return 0;
}
jdehaan
That's what I thought but it's not the problem.
rlbond
@jde: that's a typo, actually. Making the constructor of B public produces the same error.
Paul Nathan
@Paul, please fix any accidental typos in questions, so further answers don't propose the same solution :)
Johannes Schaub - litb
+5  A: 

According to TC++PL, pg 404:

A derived class can access a base class’ protected members only for objects of its own type.... This prevents subtle errors that would otherwise occur when one derived class corrupts data belonging to other derived classes.

Of course, here's an easy way to fix this for your case:

class A
{
protected:
    int data;
};

class B : public A
{
public:
    B(const A &a)
     : A(a)
    {
    }
};

int main()
{
    A a;
    B b = a;
    return 0;
}
rlbond
Umph. To be precise, this case is a dummy case only to demonstrate the problem. I actually wanted to do some fairly intensive reading of the data being passed in.
Paul Nathan
In any case you can muck around only with the protected data in the A part of this or another B object. If you have an A instance or a C instance that also derives from A, B has no special rights to those As. If you can't take this advice (make this A part of this B by copying it to be the base part of this B, which you do have access to), then you might have a design problem which might require the bigger picture to answer.
UncleBens
@rlbond: Coding after bedtime strikes again. This did solve my problem. Thanks. :)
Paul Nathan
A: 

The C++ Standard says about protected non-static members at 11.5/1

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. 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).

In addition to fixing things mentioned earlier by others (constructor of B is private), i think rlbond's way will do it fine. However, a direct consequence of the above paragraph of the Standard is that the following is possible using a member pointer, which arguably is a hole in the type system, of course

class B : public A {
public:
  B(A &a){
    int A::*dataptr = &B::data;
    data = a.*dataptr;
  }
};

Of course, this code is not recommended to do, but shows that you can access it, if you really need to (I've seen this way being used for printing out a std::stack, std::queue, std::priority_queue by accessing its protected container member c)

Johannes Schaub - litb
+1  A: 

You just shouldn't copy an A object in a B constructor. The intention is to leave the initialization of A's members to it's own constructor:

struct A { 
  A( const A& a ): data( a.data ) {}
  protected: int data; 
};

struct B : public A {
  B( const A& a ): A( a ) {}
};
xtofl