Effective C++ 36: Differentiate between inheritance of interface and inheritance of implementation
There are three types of member functions, namely pure virtual functions, simple (nonpure) functions, and nonvirtual functions.
Here is an example:
class Shape {
public:
virtual void draw() const = 0;
virtual void error(const string& msg);
int objectID() const;
...
};
class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };
A pure virtual member function means that:
1. The class must be used as a base class, and no instance of the class should be created.
2. The derived classes inherit the function interface only.
A nonpure virtual member functions means that the derived classes inherit the functions interface as well as a default implementation.
A nonvirtual member function means that the function interface and implementation is identical among all the derived classes, and should not be changed.
A nonpure vitual member function provides derived classes with a default implementation, but it allows them to inherit the implementation without explicitly saying that. This is problematic sometimes. If we want to provide a default implementation and at the same time want the derived classes to explicitly ask for it, there is two ways.
1. Declare a pure virtual function and give it a definition.
class Shape {
public:
virtual void draw() const = 0;
virtual void error(const string& msg);
int objectID() const;
...
virtual void fill() =0;
};
class Rectangle: public Shape { ... };
class Line: public Shape{ ... }; // a line can not be filled
void Shape::fill(){
[i] // a default implementation of fill[/i]
...
}
void Rectangle::fill(){
Shape::fill(); // explicitly call the funciton
}
Rectangle * r=new Rectangle;
Line * l=new Line;
r->fill(); // ok, no problem
l->fill(); // error, fill() not defined in l
2. Declare a pure virtual member function and provide a nonvirtual default one.
class Shape {
public:
virtual void draw() const = 0;
virtual void error(const string& msg);
int objectID() const;
...
virtual void fill() =0;
protected:
void DefaultFill();
};
class Rectangle: public Shape { ... };
class Line: public Shape{ ... }; // a line can not be filled
void Shape::DefaultFill(){
[i] // a default implementation of fill[/i]
...
}
void Rectangle::fill(){
DefaultFill(); // explicitly call the funciton
}
Rectangle * r=new Rectangle;
Line * l=new Line;
r->fill(); // ok, no problem
l->fill(); // error, fill() not defined in l