An idiom that allows you to do something that C++ doesn't directly support.
You can get the effect of a virtual constructor by a virtual clone()
member function (for copy constructing), or a virtual create() member
function (for the default constructor).
class Shape {
public:
virtual ~Shape() { } // A virtual destructor
virtual void draw() = 0; // A pure virtual function
virtual void move() = 0;
...
virtual Shape* clone() const = 0; // Uses the copy constructor
virtual Shape* create() const = 0; // Uses the default constructor
};
class Circle : public Shape {
public:
Circle* clone() const; // Covariant Return Types; see below
Circle* create() const; // Covariant Return Types; see below
...
};
Circle* Circle::clone() const { return new Circle(*this); }
Circle* Circle::create() const { return new Circle(); }
public:
virtual ~Shape() { } // A virtual destructor
virtual void draw() = 0; // A pure virtual function
virtual void move() = 0;
...
virtual Shape* clone() const = 0; // Uses the copy constructor
virtual Shape* create() const = 0; // Uses the default constructor
};
class Circle : public Shape {
public:
Circle* clone() const; // Covariant Return Types; see below
Circle* create() const; // Covariant Return Types; see below
...
};
Circle* Circle::clone() const { return new Circle(*this); }
Circle* Circle::create() const { return new Circle(); }
In the clone() member function, the new Circle(*this) code calls
Circle's copy constructor to copy the state of this into the newly created
Circle object. (Note: unless Circle is known to be final (AKA a leaf), you can reduce the chance of slicing by making its copy constructor protected.) In the create()
member function, the new Circle() code calls Circle's
default constructor.
Users use these as if they were "virtual constructors":
void userCode(Shape& s)
{
Shape* s2 = s.clone();
Shape* s3 = s.create();
...
delete s2; // You need a virtual destructor here
delete s3;
}
{
Shape* s2 = s.clone();
Shape* s3 = s.create();
...
delete s2; // You need a virtual destructor here
delete s3;
}
This function will work correctly regardless of whether the Shape is a
Circle, Square, or some other kind-of Shape that doesn't even exist yet.
Note: The return type of Circle's clone() member function is intentionally
different from the return type of Shape's clone() member function. This is
called Covariant Return Types, a feature that was not originally part
of the language. If your compiler complains at the declaration of Circle*
clone() const within class Circle (e.g., saying "The return type is
different" or "The member function's type differs from the base class virtual
function by return type alone"), you have an old compiler and you'll have to
change the return type to Shape* .
Note: If you are using Microsoft Visual C++ 6.0, you need to change the return
types in the derived classes to Shape* . This is because MS VC++ 6.0
does not support this feature of the language. Please do not write me
about this; the above code is correct with respect to the C++ Standard (see
10.3p5); the problem is with MS VC++ 6.0. Fortunately covariant return types
are properly supported by MS VC++ 7.0.
Source : http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.1
No comments:
Post a Comment