I’ve gone back to some refactoring for a few days to solve a problem that has been annoying me for a while. The solution is found in some C++ arcana that I always used to avoid due to porting issues, but now feel confident to try as I have not encountered a bad C++ compiler in a few years now.
Some ‘const’ member functions (those that only want to read and not write to an object) need to make innocuous changes to data members (e.g., a Set object might want to cache its last lookup in hopes of improving the performance of its next lookup, or lock access to an object via a lock object which is a member).
When this happens, the data member which will be modified should be marked as mutable (put the mutable keyword just before the data member’s declaration; i.e., in the same place where you could put const). This tells the compiler that the data member is allowed to change during a const member function.
If your compiler doesn’t support the mutable keyword, you can cast away the const‘ness of this via the const_cast keyword – but this is very very evil and bad things will happen if the moon is in the wrong phase and the cock crows four times. Seriously don’t try it – it will ruin your life. Very few C++ compilers don’t support the mutable keyword. Instead of using const_cast, better to “#define mutable” de-const your conflicting member functions and go through const-chain-reaction-hell till your compiler stops screaming at you.
You can’t do this – but I wish you could – only because it would make my scummy design neater.
But really this is an issue with the design. A mutable interface does not make much sense and it would be better that I just add that MwIPCCriticalSection to MwLockedAddressExclusiveList as a mutable member. So this is more of a problem of my bad design. I am clearly violating the rules of “HAS-A” vs “IS-IMPLEMENTED-IN-TERMS-OF” and “IS-A”
For instance, if you are designing a Radio class, and you already have the following classes implemented for you in some library: Dial, ElectricAppliance.
It is quite obvious that your Radio should be derived from ElectricAppliance. However, it is not always obvious that Radio should also be derived from Dial.
How to decide?
You can check whether there is always a 1:1 relation between the two, e.g., “do all radios HAVE one and only one dial?” You may realize that the answer is no: a radio can have no dial at all (a transmitter/receiver adjusted to a fixed frequency) or may have more than one (both an FM dial and AM dial). Its always important to use your imagination to fully flesh out the possibilities.
Hence, your Radio class should be designed as HAS Dial(s) instead of being derived from it (IS-A) . Note that the relation between Radio and ElectricAppliance is always 1:1 and corroborates the decision to derive Radio from ElectricAppliance as a Radio IS-An ElectricAppliance.
When you write software, you deal with two worlds. You deal with the world you want to model, the outside world. You also deal with a world that exists only inside the software, which involves just getting the code to work. HAS-A corresponds to something in the real world. A
Dials or a
Friends. HAS-A corresponds to the application domain. IS-IMPLEMENTED-IN-TERMS-OF never exists in the real world; it is part of the implementation domain. So you couldn’t say a
Dials. But you could say the
Radio IS-IMPLEMENTED-IN-TERMS-OF a
List of Components. There’s no
List in the real world. The
List only exists inside the software. So HAS-A is a relationship between classes that exists in the application domain. IS-IMPLEMENTED-IN-TERMS-OF is a relationship between classes that exists in the implementation domain.
There are several schools of thought of this – much like schools of kung-fu. I subscribe to the following.
If you find yourself describing relationships as “IS-A” Then you want to use public inheritance. You want to declare to the world that it conforms to a certain aspect of your model.
If you find yourself describing relationships as “IS-IMPLEMENTED-IN-TERMS-OF” then you should use private inheritance. You want to give an object an ability or behavior in the process of implementing it – You want how you did it to remain secret so that if you change your mind, nobody will notice.
It is generally thought that protected falls into its own area and nobody has yet figured out how to use it a way that conveys any useful semantic meaning that gives a programmer that happy-feel-good -rush that you get with everything in its place and a place for everything. One possible interpretation is “a combination of IS-IMPLEMENTED-IN-TERMS-OF and IS-A but for ‘my derived classes only'” – This has the effect of controlling the degree of polymorphism. Using protected can make reading the relationships within your code ambiguous – but it does have its place in the C++ pantheon of model glue.
I should have listened to my Mother’s advice and become a brain surgeon.