How I Learned to Stop Worrying and Make Everything virtual There is one small twist to overriding methods in C++ and it has to do with the keyword virtual. Only methods that are declared as virtual in the superclass can be overridden properly by subclasses. The keyword goes at the beginning of a method declaration as shown in the modifi ed version of Super that follows: class Super { public: Super(); virtual void someMethod(); protected: int mProtectedInt; private: int mPrivateInt; }; The virtual keyword has a few subtleties and is often cited as a poorly designed part of the language. A good rule of thumb is to just make all of your methods virtual. That way, you won’t have to worry about whether or not overriding the method will work. The only drawback is a very tiny performance hit. The subtleties of the virtual keyword are covered toward the end of this chapter, and performance is discussed further in Chapter 24. Even though it is unlikely that the Sub class will be extended, it is a good idea to make its methods virtual as well, just in case. class Sub : public Super { public: Sub(); virtual void someOtherMethod(); }; |
Even with the preceding measures and the best design principles, the C++ language is fundamentally unfriendly to the principle of abstraction. The syntax requires you to combine your public interfaces and private (or protected) data members and methods together in one class definition, thereby exposing some of the internal implementation details of the class to its clients. The downside of this is that if you have to add new non-public methods or data members to your class, all the clients of the class have to be recompiled. This can become a burden in bigger projects. The good news is that you can make your interfaces a lot cleaner and hide all implementation details, resulting in stable interfaces. The bad news is that it takes a bit of hacking. The basic principle is to define two classes for every class you want to write: the interface class and the implementation class. The implementation class is identical to the class you would have written if you were not taking this approach. The interface class presents public methods identical to those of the implementation class, but it only has one data member: a pointer to an implementation class object. The interface class method implementations simply call the equivalent methods on the implementation class object. The result of this is that no matter how the implementation changes, it has no impact on the public interface class. This reduces the need for recompilation. None of the clients that use the interface class need to be recompiled if the implementation (and only the implementation) changes. To use this approach with the Spreadsheet class, simply rename the old Spreadsheet class to SpreadsheetImpl. Here is the new SpreadsheetImpl class (which is identical to the old Spreadsheet class, but with a different name): |