What is the correct way to derive a class in regard to overloaded operators

O

olanglois

Hi,

I am trying to derive a new class that will add new functions but no
new data members and the base class has overloaded operators
(+,-,+=,-=,etc...) returning either (Base &) or (const Base) depending
on the operator:

class Derived : public Base
{
};

The problem occurs if I do the following

Derived d1,d2;
Derived d3 = d1+d2;

what should be the better approach?

Just add
Derived(const Base&) and
Derived &operator=(const Base&)

or should it be better to redeclare all the operators (there is a lot
of them) in the derived class and perform nothing except calling the
base class version and return Derived reference or object.

Greetings,
Olivier Langlois
http://www3.sympatico.ca/olanglois


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
V

Victor Bazarov

I am trying to derive a new class that will add new functions but no
new data members and the base class has overloaded operators
(+,-,+=,-=,etc...) returning either (Base &) or (const Base) depending
on the operator:

class Derived : public Base
{
};

The problem occurs if I do the following

Derived d1,d2;
Derived d3 = d1+d2;

what should be the better approach?

Just add
Derived(const Base&) and
Derived &operator=(const Base&)

If 'Derived' never has to know that addition, etc., is being performed,
then yes, it should be sufficient.
or should it be better to redeclare all the operators (there is a lot
of them) in the derived class and perform nothing except calling the
base class version and return Derived reference or object.

I don't think how it could help _unless_ you're foreseeing the need for
'Derived' to amend the operation somehow in the future (even if it does
not need it now). Introduction of an interface should be done sooner
rather than later.

V
 
F

Francis Glassborow

Hi,

I am trying to derive a new class that will add new functions but no
new data members and the base class has overloaded operators
(+,-,+=,-=,etc...) returning either (Base &) or (const Base) depending
on the operator:

class Derived : public Base
{
};

The problem occurs if I do the following

Derived d1,d2;
Derived d3 = d1+d2;

what should be the better approach?

You almost certainly do not want to do this by public derivation as I
very much doubt that Base was designed as a base class (i.e. I expect it
to lack a virtual dtor)

Why do you want a class that is identical to Base other than for some
added functionality? Why not just add some free functions? Your derived
class will have no special access to the base class so its member
functions cannot do anything that free functions could not do.

Before I give you any further guidance I need to know why you want to do
what you are doing.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
K

kwikius

Hi,

I am trying to derive a new class that will add new functions but no
new data members and the base class has overloaded operators
(+,-,+=,-=,etc...) returning either (Base &) or (const Base) depending
on the operator:

class Derived : public Base
{
};

The problem occurs if I do the following

Derived d1,d2;
Derived d3 = d1+d2;

what should be the better approach?

Just add
Derived(const Base&) and
Derived &operator=(const Base&)

or should it be better to redeclare all the operators (there is a lot
of them) in the derived class and perform nothing except calling the
base class version and return Derived reference or object.

IMO Always overload the operators for the derived versions if you can,
otherwise each operation is effectively losing type information and
possibly the deriveds extra data. In fact dont bother overloading them
for the base class so you get an error if you havent. Of course thats
more work... so you could try looking at ;

http://www.boost.org/libs/utility/operators.htm

which is meant to reduce the work required to implement common
operators.
(No I havent used it myself)

regards
Andy Little


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
B

Bob Hairgrove

Hi,

I am trying to derive a new class that will add new functions but no
new data members and the base class has overloaded operators
(+,-,+=,-=,etc...) returning either (Base &) or (const Base) depending
on the operator:

class Derived : public Base
{
};

The problem occurs if I do the following

Derived d1,d2;
Derived d3 = d1+d2;

what should be the better approach?

Just add
Derived(const Base&) and
Derived &operator=(const Base&)

or should it be better to redeclare all the operators (there is a lot
of them) in the derived class and perform nothing except calling the
base class version and return Derived reference or object.

Sounds like a bit of a design problem to me. What control do you have
over the design of your Base class?

Any operators or other functions which return a Base object will be a
potential problem WRT slicing.

--
Bob Hairgrove
(e-mail address removed)


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
J

James Kanze

Francis said:
You almost certainly do not want to do this by public
derivation as I very much doubt that Base was designed as a
base class (i.e. I expect it to lack a virtual dtor)
Why do you want a class that is identical to Base other than
for some added functionality? Why not just add some free
functions? Your derived class will have no special access to
the base class so its member functions cannot do anything that
free functions could not do.

Good point. I just automatically assumed that one of his goals
was to have a new type, which of course supposes a class, but
if that's not the case, free functions are the way to go.

There is also one very special case I've used from time to time:
derivation to provide new constructors. But in such cases,
"slicing" is perfectly acceptable -- once the constructor has
run, I simply use the object as if it were a base, copying the
base part, etc. without worrying that the class was originally
constructed as a derived.

--
James Kanze (e-mail address removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
J

James Kanze

I am trying to derive a new class that will add new functions
but no new data members and the base class has overloaded
operators (+,-,+=,-=,etc...) returning either (Base &) or
(const Base) depending on the operator:
class Derived : public Base
{
};

I'm not sure what you're doing is a good idea. Operator
overloading largely depends on value semantics, where as
derivation practically requires reference semantics to work
correctly.
The problem occurs if I do the following
Derived d1,d2;
Derived d3 = d1+d2;
what should be the better approach?
Just add
Derived(const Base&) and
Derived &operator=(const Base&)
or should it be better to redeclare all the operators (there
is a lot of them) in the derived class and perform nothing
except calling the base class version and return Derived
reference or object.

I'm not sure why you are deriving, so it's hard to say, but I
suspect that the cleanest approch would use containment, not
inheritance, and redefine every operator. It's a lot of copy
work, of course, but one way or the other, I don't see any way
around it.

--
James Kanze (e-mail address removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
P

Philippe Amarenco

Hi,

I am trying to derive a new class that will add new functions but no
new data members and the base class has overloaded operators
(+,-,+=,-=,etc...) returning either (Base &) or (const Base) depending
on the operator:

class Derived : public Base
{
};

The problem occurs if I do the following

Derived d1,d2;
Derived d3 = d1+d2;

what should be the better approach?

Just add
Derived(const Base&) and
Derived &operator=(const Base&)

or should it be better to redeclare all the operators (there is a lot
of them) in the derived class and perform nothing except calling the
base class version and return Derived reference or object.

if you can change the code you of Base and want to create a few
different Derived class, you probably want to have your base class be
a template parametered by your derived class:

template <typename T>
class Base {
T& operator+(T&);
// ...
};

class Derived : public Base<Derived> {
// ...
};

--
Philippe Amarenco, aka Phix
epita 2007 - GISTR - LSE - EpX

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
M

Marek Vondrak

You almost certainly do not want to do this by public derivation as I
very much doubt that Base was designed as a base class (i.e. I expect it
to lack a virtual dtor)

Why do you want a class that is identical to Base other than for some
added functionality? Why not just add some free functions? Your derived
class will have no special access to the base class so its member
functions cannot do anything that free functions could not do.

Before I give you any further guidance I need to know why you want to do
what you are doing.

Both Base and Derived are probably meant to be used as value types that are
allocated on the stack (hence no need for a virtual destructor). Base class
might wrap an integral type to a class and define operations so that it
could be used in place of the native integer type and Derived might
public-inherit from Base and extend its functionality by adding a support
for serialization, etc. The question of reimplementing operations in Derived
is pretty reasonable in this context then.

As for the original question... I am afraid that all operations have to be
reimplemented in Derived as stubs delegating the calls to Base, because
Derived's operator+() should really return Derived, for example.

-- Marek




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
O

olanglois

Hi,

Thanks all for your input. I am going to answer to your questions to
keep the ball rolling.

I have 100% control over the base class and I'm willing to change
anything to come up with the best solution. Here is some background on
what I try to achieve. I have class with a default constructor defined
like this:

Base( int param = DEFAULTVAL);

I have reused that class in a new program where I would like to
override DEFAULTVALUE to something else when I create arrays of this
class so the way I came up to solve this problem is by doing this:

templace<int DEFAULT = DEFAULTVAL>
class Derived : public Base
{
Derived( int param = DEFAULT );
};

I did not changed Base to become a template because only object
construction needs to be templatized.

However by doing so, I introduced a new problem with Base operators.
Since my original post, for now, to solve the problem is this:

1- Made Base abstract
2- Keep operators returning references in Base and move operators
returning objects to Derived.
3- Added the constructor Derived( const Base &) Derived( const
Derived&) (By experience, I found out that I needed both)
4- Added the assignment opertor=(const Base &) (otherwise a temporary
Derived object is created...)

Thanks for your help!
Greetings,
Olivier Langlois
http://www3.sympatico.ca/olanglois


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
F

Francis Glassborow

Marek Vondrak said:
Both Base and Derived are probably meant to be used as value types that are
allocated on the stack (hence no need for a virtual destructor). Base class
might wrap an integral type to a class and define operations so that it
could be used in place of the native integer type and Derived might
public-inherit from Base and extend its functionality by adding a support
for serialization, etc. The question of reimplementing operations in Derived
is pretty reasonable in this context then.

In the circles I move in, public inheritance of a value type is
considered suspect. Public inheritance represents an 'is-a' relationship
with the concept of LSP.

If the derived type is intended to be a full value type the risk of
slicing (i.e unintentional conversion to the base type) provides an
opportunity for subtle bugs.

Of course what we really need is strong typedefs such as those being
considered for C++0x where issues of convertability are tackled by the
form of the typedef.



--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
K

kanze

I have 100% control over the base class and I'm willing to
change anything to come up with the best solution. Here is
some background on what I try to achieve. I have class with a
default constructor defined like this:
Base( int param = DEFAULTVAL);
I have reused that class in a new program where I would like
to override DEFAULTVALUE to something else

That's trivial... Just do it, and don't worry about the slicing.
The Base operators will still take a Derived, and you should use
Base just about everywhere except when you need to construct.
(Definitions like:
Base obj = Derived() ;
are perfectly legal.)
when I create arrays of this class

As long as the arrays are std::vector, no problem. A C style
array, on the other hand, might be.
so the way I came up to solve this problem is by doing this:
templace<int DEFAULT = DEFAULTVAL>
class Derived : public Base
{
Derived( int param = DEFAULT );
};
I did not changed Base to become a template because only
object construction needs to be templatized.
However by doing so, I introduced a new problem with Base
operators. Since my original post, for now, to solve the
problem is this:
1- Made Base abstract

I'm not sure what this buys you.
2- Keep operators returning references in Base and move operators
returning objects to Derived.
3- Added the constructor Derived( const Base &) Derived( const
Derived&) (By experience, I found out that I needed both)

The compiler should provide the second, if you don't.
4- Added the assignment opertor=(const Base &) (otherwise a
temporary Derived object is created...)

You also need to make the destructor of Base virtual. I'd also
add a private operator new[] to Derived -- in no case can you
allow the construction of C style arrays of Derived on the heap
(since they will fatally end up assigned to a Base*).

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
A

Allan W

kanze said:
(e-mail address removed) wrote: [snip]
4- Added the assignment opertor=(const Base &) (otherwise a
temporary Derived object is created...)

You also need to make the destructor of Base virtual. I'd also
add a private operator new[] to Derived -- in no case can you
allow the construction of C style arrays of Derived on the heap
(since they will fatally end up assigned to a Base*).

Yeah, but...

if sizeof(Base)==sizeof(Derived), and if ~Derived() has an empty
body (i.e. it does nothing but call ~Base()), then there isn't
likely to be a problem, not even with new Derived[] -- is there?
Yes, I am aware that it violates the standard... but show me a
program that does this, and a compiler that makes it crash?

#include <iostream>
#include <ostream>

struct Base {
int x;
// whatever...
Base(int z=0) : x(z) {}
~Base() { std::cout << "Destroy: " << x << '\n'; }
};

struct Derived : public Base { // No Multiple Inheritance
static int nextX;
Derived() : Base(nextX++) {}
// No new non-static data members
//~Derived(); -- Does not explicitly define the destructor
};
int Derived::nextX = 100;

int main() {
{
Base a(10);
Base *b = new Derived[5];

delete[] b; // Deleting with wrong data type, violates
standard...
// but is it EVER a problem in these limited situations?
// Calls ~Base() directly, instead of going through
~Derived()...
// But all ~Derived() does is forward to ~Base(), right?
}

std::cout << "Fin" << std::endl;
}

Even Comeau doesn't complain about this code
(haven't tried running it with Comeau, though)
I did try it with Microsoft, it gave the obvious results.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
J

James Kanze

Allan said:
kanze said:
(e-mail address removed) wrote: [snip]
4- Added the assignment opertor=(const Base &) (otherwise a
temporary Derived object is created...)
You also need to make the destructor of Base virtual. I'd
also add a private operator new[] to Derived -- in no case
can you allow the construction of C style arrays of Derived
on the heap (since they will fatally end up assigned to a
Base*).
Yeah, but...
if sizeof(Base)==sizeof(Derived), and if ~Derived() has an
empty body (i.e. it does nothing but call ~Base()), then there
isn't likely to be a problem, not even with new Derived[] --
is there? Yes, I am aware that it violates the standard...
but show me a program that does this, and a compiler that
makes it crash?

In the absense of further derivation, I think in fact it is
safe. I even once toyed with the idea making a proposal to the
standard to guarantee that it work -- basically, defining
"trivial derivation" as adding new constructors and nothing
else, and then saying that it would work in the case of trivial
derivation. In the end, it seemed too much bother, and too much
special case. And not really that useful.
#include <iostream>
#include <ostream>
struct Base {
int x;
// whatever...
Base(int z=0) : x(z) {}
~Base() { std::cout << "Destroy: " << x << '\n'; }
};
struct Derived : public Base { // No Multiple Inheritance
static int nextX;
Derived() : Base(nextX++) {}
// No new non-static data members
//~Derived(); -- Does not explicitly define the destructor
};
int Derived::nextX = 100;
int main() {
{
Base a(10);
Base *b = new Derived[5];
delete[] b; // Deleting with wrong data type, violates
standard...
// but is it EVER a problem in these limited situations?
// Calls ~Base() directly, instead of going through
~Derived()...
// But all ~Derived() does is forward to ~Base(), right?
}
std::cout << "Fin" << std::endl;
}
Even Comeau doesn't complain about this code
(haven't tried running it with Comeau, though)
I did try it with Microsoft, it gave the obvious results.

Well, it's undefined behavior, and I doubt you'll find a
compiler which complains. But because it's undefined behavior,
the fact that a compiler gives the obvious results once doesn't
prove anything. (In theory, at least. In practice, knowing how
compilers work, I can't imagine an implementation where it would
fail.)

--
James Kanze (e-mail address removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 

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,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top