Search code examples
c++inheritanceprotected

When protected protects too much


This is about "protected", which is explained as: "When a class inherits another one, the members of the derived class can access the protected members inherited from the base class." However, look at this code:

class Base {
public:
  Base( int m ) : member(m){}
protected:
  int member;
};

class Derived : public Base { // one of several subclasses
public:
  Derived( int m ) : Base(m), value(10) {}
  int diff( Base x ){
    return value - x.member;
  }
private:
  int value;
};

Derived accesses "x.member", which is protected in its Base class, right? But the compiler flags an error, "Base::member is protected". And, after mulling this over for a minute, I had to agree with the compiler.

And here comes the question: How do I make this work, with a minimum loss of information hiding?

  1. Clearly, making "member" public compiles, but it's against the original intent.
  2. Using the "friend" mechanism in Base to let subclasses access "member" (and everything else that's private and protected) is even worse (apart from the dumb binding of a superclass to its own subclasses - a maintenance nightmare).
  3. In the simple example, a public int getMember(){ return member; } would be acceptable. But if member's type is a X*, the best you can do is a public const X* getMember(){...}.

Did I miss something?


Solution

  • You can use a static protected accessor:

    class Base {
    public:
      Base( int m ) : member(m){}
    private:
      int member;
    protected:
      static int GetMember(const Base &b)
      { return b.member; }
    };
    
    class Derived : public Base { // one of several subclasses
    public:
      Derived( int m ) : Base(m), value(10) {}
      int diff( Base &x ){ //beware of your slicing!
        return value - GetMember(x);
      }
    private:
      int value;
    };
    

    Now let me add my idea of why C++ access control works this way...

    Access control in C++ is not about information hiding. It is about encapsulation. That is, plainly speaking, you filter out the access to any member that can break the class if used incorrectly.

    In an ideal class

    • public members cannot be used to break the object.
    • private members know what they are doing.

    As you see, in my scheme there is little place for protected members:

    • protected members are used to implement the inheritance interface, if any.

    And even less place for protected member variables.

    So make your variable private, and write a protected accessor. The accessor must be static to be able to be used from the derived object.