Polymorphism
Polymorphism is the technique of defining a common interface for a hierarchy of classes. To support this, a derived object is allowed wherever a base object is expected. For example, String s="Hello";
Vector<char> v=s; // Discards derived part of s to convert
Vector<char>* p=&s; // p points to base part of s
try {throw s;} catch(Vector<char> x) {} // Caught with x set to base part of s
s=Vector<char>(5); // Error, can't convert base to derived
// Allow output of Vector<char> using normal notation
ostream& operator << (ostream& out, const Vector<char>& v) {
copy(v.begin(), v.end(), ostream_iterator<char>(out, "")); // Print v to out
return out; // To allow (cout << a) << b;
}
cout << s; // OK, v refers to base part of s
ofstream f("file.txt");
f << s; // OK, ofstream is derived from ostream
A derived class may redefine inherited member functions, overriding any function with the same name, parameters, return type, and const-ness (and hiding other functions with the same name, thus the overriding function should not be overloaded). The function call is resolved at compile time. This is incorrect in case of a base pointer or reference to a derived object. To allow run time resolution, the base member function should be declared virtual. Since the default destructor is not virtual, a virtual destructor should be added to the base class. If empty, no copy constructor or assignment operator is required. Constructors and = are never virtual.
class Shape {
public:
virtual void draw() const;
virtual ~Shape() {}
};
class Circle: public Shape {
public:
void draw() const; // Must use same parameters, return type, and const
};
Shape s; s.draw(); // Shape::draw()
Circle c; c.draw(); // Circle::draw()
Shape& r=c; r.draw(); // Circle::draw() if virtual
Shape* p=&c; p->draw(); // Circle::draw() if virtual
p=new Circle; p->draw(); // Circle::draw() if virtual
delete p; // Circle::~Circle() if virtual
An abstract base class defines an interface for one or more derived classes, which are allowed to instantiate objects. Abstractness can be enforced by using protected (not private) constructors or using pure virtual member functions, which must be overridden in the derived class or else that class is abstract too. A pure virtual member function is declared =0; and has no code defined.
class Shape {
protected:
Shape(); // Optional, but default would be public
public:
virtual void draw() const = 0; // Pure virtual, no definition
virtual ~Shape() {}
};
// Circle as before
Shape s; // Error, protected constructor, no Shape::draw()
Circle c; // OK
Shape& r=c; r.draw(); // OK, Circle::draw()
Shape* p=new Circle(); // OK
Run time type identification
C++ provides for run time type identification, although this usually indicates a poor design. dynamic_cast<T>(x) checks at run time whether a base pointer or reference is to a derived object, and if so, does a conversion. The base class must have at least one virtual function to use run time type checking. #include <typeinfo> // For typeid()
typeid(*p)==typeid(T) // true if p points to a T
dynamic_cast<T*>(p) // Convert base pointer to derived T* or 0.
dynamic_cast<T&>(r) // Convert base reference to derived T& or throw bad_cast()
For example, class B {public: virtual void f(){}};
class D: public B {public: int x;} d; // Bad design, public member in D but not B
B* p=&d; p->x; // Error, no B::x
D* q=p; q->x; // Error, can't convert B* to D*
q=(D*)p; q->x; // OK, but reinterpret_cast, no run time check
q=dynamic_cast<D*>(p); if (q) q->x; // OK