The protected portion of the class interface can only be accessed by derived classes. This feature is helpful but can't express the totality of the relationship between a class and its derived classes. Other important factors include which functions might and might not be overridden, and how they must behave. It is also crucial to consider the relationship between member functions; some of them might need to be overridden in groups to preserve the class' semantics.
The bottom line is this: design your interface to derived classes so that a derived class that uses every supported aspect of that interface (including overriding virtual functions) doesn't compromise the integrity of your public interface. Because C++ can't express the complete interface to derived classes, it is quite easy for a developer who derives a class from yours to violate your class invariants no matter what you do. Make it clear through your interface and documentation how to make a derived class that preserves those invariants.
You can avoid this problem by making the function in question a hook for derived classes to override, and moving the inherited version into the code that calls the hook. This only works for one level of derivation; overriding the same function at multiple levels is error prone. Hook functions are the cleanest way to interact with derived classes. For more information, see "Virtual functions" on page 66.
This is why a base class designer must make it very clear how its virtual functions can be overridden, not just whether they can be. Keep in mind that just because a function is virtual does not mean you can override it (see "Virtual functions" on page 66).
A common practice is to make get functions nonvirtual and set functions virtual. This makes internal access fast, but gives subclasses a chance to react to changes.
NOTE
An example of where this rule of thumb is intentionally violated is a class with a write-only attribute. If the data member being changed can't be read through the class interface (for example, a cache), it is acceptable for the class to set it directly even if there is a virtual interface function that also sets it. However, this is rather unusual; in most cases, if you do not use your own virtual get and set functions you're making a mistake.
What this amounts to is, don't assume that putting
Expected calls
A simple example of not preserving the base class invariants is when a derived class overrides a function that the base class is counting on the derived class calling. If the derived class doesn't call the base class function, or calls it incorrectly, the base class' invariants are violated and unpredictable results can occur. Group override
Sometimes a base class requires that you override virtual functions in groups; correct interaction between the base and its derived classes requires that you override all of the functions in a group together. Getters and setters
Get and set functions should not be virtual unless they are used by the class that defines them. If the base class does direct field access, you usually can't override the get and set functions correctly. Suppose a derived class overrides a set function so that it can update its own information whenever the base class information is updated. But, if the base class changes the corresponding field directly, a derived class never gets notified and its information becomes stale.virtual
or protected
in front of your member functions defines the interface to derived classes. That interface must be as carefully designed and documented as the client interface.
[Contents]
[Previous]
[Next]
Click the icon to mail questions or corrections about this material to Taligent personnel.
Generated with WebMaker