Is there some rule of thumb for that decision? I always stuck with this question. Even if I know that currently I don't need the result of overriden method, how can I be sure that in the future the overriden method won't be modified? For example, there is a possibility that author of the parent class that I'm extending will decide to implement some side-effect in the method that I'm overriding and without that side-effect the state of the object will be incorrect.
views:
156answers:
4There's no hard-and-fast rule. Sometimes you don't want to call the superclass implementation, and other times you do - doing some work before and/or after the superclass call.
IMO, the default decision should always be to call the superclass and then handle any different behavior. This is for all of the reasons that you mention (it may be needed now, but if not, it may be needed in the future).
The one exception is the case where you KNOW ahead of time that the superclass will be doing things that you specifically do not want. But those issues point at design problems anyway. Its very suspicious to see subclasses not calling their parent.
STL library usually don't do any non-trivial work in default implementation of virtual functions. They solve problem in question by wrapping calls to virtual functions with non-virtual ones. Such behavior presumes that extender of base class will never call default implementation of virtual function (as there is no need to).
If the designer of base class decides to extend the functionality of some virtual member function, he should write such a wrapper, add new functionality and a call to virtual function there, and use that wrapper everywhere. This conception allows extension of functionality and interface of base class without descendant classes breaking. See std::streambuf for example.
This way base class designer may also attain Alexandrescu's recommendation of not making virtual functions public.
There are two main reasons to call your base class's implementation in an override of one of its methods:
You want to extend the behavior of the base class.
In this case, it's often easier to rely on the base class to perform the brunt of the work, and to just add the additional work on top. This decision is usually pretty easy to make.
The base implementation has some side effect that is either desired or required.
This is sometimes a little harder to determine. External side effects will hopefully be documented, and you should be able to determine whether or not they should occur.
What's sometimes difficult to determine, which you allude to, is if a function has internal side effects. That is, if it modifies some private state. A simple example:
class Car{
bool engineRunning;
public virtual void StartEngine(){
TurnIgnitionKey();
engineRunning = true; // this is the internal side effect
}
public void DriveAround(){
if(!engineRunning)
throw new InvalidOperationException("You have to start the engine first.");
// implement driving around
}
}
class S2000 : Car{
public override void StartEngine(){
PushStartButton();
}
}
In this example, because S2000
's implementation of StartEngine()
doesn't call Car
's implementation, then DriveAround()
will fail. Without having the source code to Car
, or very good documentation, the author of S2000
would likely not know that the base's StartEngine
routine should be called.
For this reason, Car
is not an ideal implementation. Better would be something like:
class Car{
bool engineRunning;
public void StartEngine(){ // not virtual
StartEngineInternal();
engineRunning = true;
}
protected virtual void StartEngineInternal(){
TurnIgnitionKey();
}
}
In this example, the internal side effect is protected by being contained within a non-overridable wrapper function that calls the protected virtual core implementation. This way, the state of the object is not dependent on inheritors calling the base method, leaving the decision on whether or not to do so in the hands of the inheritor, where it belongs.