سؤال

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?

هل كانت مفيدة؟

المحلول 2

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.

نصائح أخرى

You can keep the protected attributes, add the getter functions as you mentioned. As for a protected pointer attribute, the getter would make sure that int (or a const ref for a large object) is returned and you can do the difference in a function template that takes Derived and Base arguments then (getters give you the values for the calculation).

Protecting data attributes allows direct access to the protected attribute within the derived class. What you tried is to access a private attribute of another object. This part of your code:

int diff( Base x ){
    return value - x.member;
  }

would be equivalent to writing this in main:

Base x; 
cout << x.member << endl;

Here is an example of how to solve the problem using the option 3 you suggested yourself, with a derived class PointerDerived that is using pointers for the storage:

#include <iostream>

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

  int getMember() const
  {
      return member; 
  }

protected:
  int member;
};

class Derived : public Base { // one of several subclasses
public:
  Derived( int m ) : Base(m), value(10) {}

  int getValue() const
  {
      std::cout << "protected = " << member << std::endl;
      return value; 
  }

private:
  int value;
};

class PointerDerived : public Base { // one of several subclasses
public:

  PointerDerived( int m ) : Base(m), value(new int (10)) {}

  int getValue() const
  {
      std::cout << "protected = " << member << std::endl;
      return *value; 
  }

  ~PointerDerived()
  {
      delete value; 
      value = nullptr;  
  }

private:
  int* value;
};

template<typename Derived, typename Base>
int diff(const Derived& d, const Base& b)
{
    return d.getValue() - b.getMember(); 
}

using namespace std;

int main(int argc, const char *argv[])
{
    PointerDerived p(23); 
    Base q(1);

    cout << diff(p, q) << endl; 

    return 0;
}

Compile the program with -std=c++11 because of the nullptr, or change it to NULL.

You make the diff a function template, so that you don't have to overload it for each and every derived class, and you let the derived class handle the storage and access to it as does for example PointerDerived.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top