Virtual Constructor and Covariant Return Types

A

Alex Vinokur

Hello,

Here is some program with virtual constructors.

Is there any difference between
* clone1() vs. clone2()
* create1() vs. create2() ?

It seems that it should be.

The program has been successfully compiled and invocated.

------ C++ code : BEGIN ------
class Shape
{
public:
virtual ~Shape() {}

virtual void draw() = 0;

virtual Shape* clone1() const = 0;
virtual Shape* create1() const = 0;

virtual Shape* clone2() const = 0;
virtual Shape* create2() const = 0;

};

class Circle : public Shape
{
public:
void draw() {}

// --- Covariant Return Types ---
Circle* clone1() const { return new Circle(*this); }
Circle* create1() const { return new Circle();}
// ------------------------------

// --- NonCovariant Return Types ---
Shape* clone2() const { return new Circle(*this); }
Shape* create2() const { return new Circle();}
// ---------------------------------

};

void userCode(Shape& s)
{
Shape* sclone1 = s.clone1();
Shape* screate1 = s.create1();

Shape* sclone2 = s.clone2();
Shape* screate2 = s.create2();


sclone1->draw();
screate1->draw();

sclone2->draw();
screate2->draw();

delete sclone1;
delete screate1;

delete sclone2;
delete screate2;

}

int main()
{
Shape* s = new Circle;
userCode (*s);

delete s;

return 0;
}
------ C++ code : END --------
 
B

Buster

Alex said:
Here is some program with virtual constructors.

Is there any difference between
* clone1() vs. clone2()
* create1() vs. create2() ?
[...]

// --- Covariant Return Types ---
Circle* clone1() const { return new Circle(*this); }
Circle* create1() const { return new Circle();}
// ------------------------------

// --- NonCovariant Return Types ---
Shape* clone2() const { return new Circle(*this); }
Shape* create2() const { return new Circle();}
// ---------------------------------

[...]

Sorry, am I missing the point? You can see the what the difference is.
 
A

Alf P. Steinbach

* "Alex Vinokur said:
Here is some program with virtual constructors.

Would be nice if people stopped using that term, even though it is
in the FAQ... ;-)


Is there any difference between
* clone1() vs. clone2()
* create1() vs. create2() ?

class Circle : public Shape
{
public:
void draw() {}

// --- Covariant Return Types ---
Circle* clone1() const { return new Circle(*this); }
Circle* create1() const { return new Circle();}
// ------------------------------

// --- NonCovariant Return Types ---
Shape* clone2() const { return new Circle(*this); }
Shape* create2() const { return new Circle();}
// ---------------------------------

};

The difference is not in what they do, but in the static type-checking
available for client code.

Btw. it's a good idea to indicate deallocation responsibility in some
way, e.g. by returning smart-pointers instead of raw pointers.
 
K

Karl Heinz Buchegger

Alex said:
Hello,

Here is some program with virtual constructors.

Is there any difference between
* clone1() vs. clone2()
* create1() vs. create2() ?

It seems that it should be.

No. Semantically they are equivalent.
Covariant return types are just a compile time
only thing. The created object is of type Circle
and thus both functions return a Circle*. Even
if in one of them the Circle* is called Shape*


The thing with covariant return types is this:
sometimes you call the virtual function and you know
exactly what the returned pointer must be.
Example:

Circle A;

Circle* B = A.clone();

The thing returned from A.clone() must be Circle* since A is a Circle.
It can't be anything else. Yet you are forced (without covariant
return types) to use a cast:

Circle* B = dynamic_cast< Circle* >( A.clone() );

or to make B a different type:

Shape* B = Circle.clone();

because the return value in the clone() function in the base class,
specified Shape* as return value.

This is where covariant return types help: If you (and the
compiler) know exactly which one of the derived class pointer
is returned, you can use it directly without going though the hassle
of casting.
 
A

Alex Vinokur

Buster said:
Alex said:
Here is some program with virtual constructors.

Is there any difference between
* clone1() vs. clone2()
* create1() vs. create2() ?
[...]

// --- Covariant Return Types ---
Circle* clone1() const { return new Circle(*this); }
Circle* create1() const { return new Circle();}
// ------------------------------

// --- NonCovariant Return Types ---
Shape* clone2() const { return new Circle(*this); }
Shape* create2() const { return new Circle();}
// ---------------------------------

[...]

Sorry, am I missing the point? You can see the what the difference is.

clone1() and create1() return Circle*,
clone2() and create2() return Shape*.

The question is if we can use
* create2() instead of create1(),
* clone2() instead of clone1()
with the same results.
 
B

Buster

Alex said:
clone1() and create1() return Circle*,
clone2() and create2() return Shape*.

Exactly that.
The question is if we can use
* create2() instead of create1(),
* clone2() instead of clone1()
with the same results.

Yes.

Circle c;
Circle * p = static_cast <Circle *> (c.create2 ());
Circle * q = static_cast <Circle *> (c.clone2 ());

You should probably declare the "create" functions static and the
constructors private or protected.
 
B

Buster

Karl said:
Alex Vinokur wrote:
The thing returned from A.clone() must be Circle* since A is a Circle.
It can't be anything else. Yet you are forced (without covariant
return types) to use a cast:

Circle* B = dynamic_cast< Circle* >( A.clone() );

static_cast is sufficient, since we already know the dynamic type.
dynamic_cast is for querying the type.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top