Even though constructors cannot actually be virtual, a very simple idiom can be used to have the same effect. This idiom, called the virtual constructor idiom, allows the creation of an object without specifying the object's exact type. For example, a base class can have a virtual clone() const member function (for creating a new object of the same class and for copying the state of the object, just like the copy constructor would do) or a virtual createSimilar() const member function (for creating a new object of the same class, just as the default constructor would do). Following is an example of this idiom (the return type is an auto_ptr to help prevent memory leaks and wild pointers; see FAQ 32.01). #include <new> #include <memory> #include <iostream> using namespace std; class Shape; typedef auto_ptr<Shape> ShapePtr; class Shape { public: Shape() throw(); virtual ~Shape() throw(); virtual void draw() const throw() = 0; virtual ShapePtr clone() const throw(bad_alloc) = 0; virtual ShapePtr createSimilar() const throw(bad_alloc) = 0; }; Shape::Shape() throw() { } Shape::~Shape() throw() { } class Circle : public Shape { public: Circle(int radius=0) throw(); virtual void draw() const throw(); virtual ShapePtr clone() const throw(bad_alloc); virtual ShapePtr createSimilar() const throw(bad_alloc); protected: int radius_; }; Circle::Circle(int radius) throw() : Shape(), radius_(radius) { } void Circle::draw() const throw() { cout << "Circle: radius=" << radius_ << '\n'; } ShapePtr Circle::createSimilar() const throw(bad_alloc) { return new Circle(); } ShapePtr Circle::clone() const throw(bad_alloc) { return new Circle(*this); } In Circle::createSimilar() const and Circle::clone() const, the kind-of relationship allows the conversion from a Circle* to a Shape*, then the Shape* is converted to an auto_ptr<Shape> (that is, to a ShapePtr) by the auto_ptr's constructor. In Circle::clone() const, the expression new Circle(*this) calls Circle's copy constructor, since *this has type const Circle& inside a const member function of class Circle. Users can use clone and/or createSimilar as if they were virtual constructors. An example follows. void userCode(ShapePtr s) throw() { cout << "userCode() number 1: "; s->draw(); ShapePtr copy = s->clone(); cout << "userCode() number 2: "; copy->draw(); ShapePtr similar = s->createSimilar(); cout << "userCode() number 3: "; similar->draw(); } <-- 1 int main() { ShapePtr c(new Circle(42)); cout << "main() number 1: "; c->draw(); userCode(c); <-- 2 }
The output of this program follows. main() number 1: Circle: radius=42 userCode() number 1: Circle: radius=42 userCode() number 2: Circle: radius=42 userCode() number 3: Circle: radius=0 |