The easiest solution is to define a protected: interface in addition to the public: interface. A class hierarchy is more resilient to changes if it has two distinct interfaces for two distinct sets of users.
Both interfaces must be fully specified. For instance, the actual raw data of a class could be private: with a set of protected: inline member functions for accessing this data. These inline member functions define an interface between the derived classes and the raw bits of the base class. Then the private: data of the base class could be changed within reasonable bounds without affecting the derived classes. It would still be necessary to recompile the derived classes after a change to the base class, though the source code of the derived class would not need to be changed unless the protected: interface is modified in a nonbackward compatible manner. For example, suppose class Base has an int data member. Base can ensure that derived classes do not rely on the specific data structure by making the data structure private: (in this case, a simple int) and defining inline protected: members for accessing these data. Derived class Derived accesses the value using these protected: inline member functions. class Base { public: Base() throw(); protected: void storeValue(int value) throw(); int retrieveValue() const throw(); private: int value_; }; inline Base::Base() throw() : value_(37) { } inline void Base::storeValue(int value) throw() { value_ = value; } inline int Base::retrieveValue() const throw() { return value_; } class Derived : public Base { public: void f(int i) throw(); }; void Derived::f(int i) throw() { storeValue(i); } int main() { Derived d; d.f(42); } |