FAQ 21.07 What is a virtual constructor?

graphics/new_icon.gif

The virtual keyword cannot be applied to a constructor since a constructor turns raw bits into a living object, and until there is a living object against which to invoke a member function, the member function cannot possibly work correctly. Instead of thinking of constructors as normal member functions on the object, imagine that they are static member functions (see FAQ 16.05) that create objects.

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 } 

(1) copy and similar are automatically deleted here (see FAQ 2.07)

(2) Because of auto_ptr's copy semantics, c will be NULL here (see FAQ 2.11)

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 


C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net