Operator overloading and inheritence: Code critique please

G

gorda

Hello,

I am playing around with operator overloading and inheritence,
specifically overloading the + operator in the base class and its
derived class.

The structure is simple: the base class has two int memebers "dataA",
"dataB". The derived class has an additional int member "dataC". I am
simply trying to overload the + operator so that 'adding' two objects
will sum up the corresponding int members.

Can someone tell me if I'm overloading the + operator correctly in the
derived class, or if there is a better way to do it?

#include <iostream>
using namespace std;

class base
{
public:
//base int members
int dataA,dataB;

//default constructor
base():dataA(0),dataB(0)
{}

//overloaded constructor
base(int x, int y): dataA(x), dataB(y)
{}

//operator + in the base class
base operator+(const base& rhs) const
{
base tmp;
tmp.dataA = this->dataA + rhs.dataA;
tmp.dataB = this->dataB + rhs.dataB;

return tmp;
}

};

class derived: public base
{
public:
//additonal derived class int member
int dataC;

//default constructor
derived():base()
{
dataC=0;
}

//overloaded constructor
derived(int x, int y, int z):base(x,y)
{
dataC=z;
}

//copy constructor
derived(const derived& t): base(t)
{
this->dataC = t.dataC;
}

//operator + in the derived class
derived operator+(const derived &rhs) const
{
derived tmp;
base *pbase;

//!!!****is this the best way for my purpose?****!!!//
pbase=&tmp;
*pbase = base::eek:perator+(rhs);

tmp.dataC = this->dataC + rhs.dataC;
return tmp;
}

//print values
void getValues()
{
cout << dataA <<" "<< dataB <<" "<< dataC << endl;
}
};

int main()
{
derived a(10,20,30);
derived b(1,2,3);
derived c;

c = a + b;
c.getValues();
}


The output is as expected: 11 22 33

If someone can suggest a better way to overload the + operator in the
derived class if I haven't done it correctly, i'd appreciate any
responses.

-Thanks
Gorda Smith
 
A

Ali Cehreli

Can someone tell me if I'm overloading the + operator correctly in the
derived class, or if there is a better way to do it?

I already replied to this on the moderated group. Here is a quick
response for speed:

- No public members please :)

- I recommend that you implement operator+ as a free
function thas is implemented in terms of a member operator+=:

base operator+ (base lhs, base const & rhs)
{
return lhs += rhs;
}

Note that lhs is passed-by-value for allegedly improving the chances
of optimization :)

- For derived, if you go the operator+= way, then it's simpler:

derived & operator+= (derived const & other)
{
base::eek:perator+=(other);
c_ += other.c_;
return *this;
}

You will need a similar operator+ for derived of course:

derived operator+ (derived lhs, derived const & rhs)
{
return lhs += rhs;
}

Ali
 
A

Ali Cehreli

I am playing around with operator overloading and inheritence,
specifically overloading the + operator in the base class and its
derived class.

The structure is simple: the base class has two int memebers "dataA",
"dataB". The derived class has an additional int member "dataC". I am
simply trying to overload the + operator so that 'adding' two objects
will sum up the corresponding int members.

First of all, inheritance may not be the best approach in this
case. At least for the example given below, it seems to be better if
'derived' contains 'base' as a member. You need to ask yourself
whether 'derived' really "IS A" 'base' in this design.
Can someone tell me if I'm overloading the + operator correctly in the
derived class, or if there is a better way to do it?

#include <iostream>
using namespace std;

class base
{
public:
//base int members
int dataA,dataB;

No public members please :)
//default constructor
base():dataA(0),dataB(0)
{}

//overloaded constructor
base(int x, int y): dataA(x), dataB(y) {}

//operator + in the base class
base operator+(const base& rhs) const {
base tmp;
tmp.dataA = this->dataA + rhs.dataA;
tmp.dataB = this->dataB + rhs.dataB;

return tmp;

Why default construct and then assign to members? This is better:

return base(dataA + rhs.dataA,
dataB + rhs.dataB);

Having said this, I recommend that you implement operator+ as a free
function thas is implemented in terms of a member operator+=:

base operator+ (base lhs, base const & rhs)
{
return lhs += rhs;
}

Note that lhs is passed-by-value for allegedly improving the chances
of optimization :)
};

class derived: public base
{
public:
//additonal derived class int member
int dataC; [...]
//operator + in the derived class
derived operator+(const derived &rhs) const {
derived tmp;
base *pbase;

//!!!****is this the best way for my purpose?****!!!//
pbase=&tmp;
*pbase = base::eek:perator+(rhs);

tmp.dataC = this->dataC + rhs.dataC;
return tmp;
}

Seems to be ok, but if you go the operator+= way, then it's simpler:

derived & operator+= (derived const & other)
{
base::eek:perator+=(other);
c_ += other.c_;
return *this;
}

You will need a similar operator+ for derived of course:

derived operator+ (derived lhs, derived const & rhs)
{
return lhs += rhs;
}

Ali
 
D

Daniel Frey

gorda said:
If someone can suggest a better way to overload the + operator in the
derived class if I haven't done it correctly, i'd appreciate any
responses.

I suggest you think about implementing operator+= instead of operator+.
After you have done that, implement operator+ in terms of operator+=.
It's a nice way of reducing some of the complexity you are experiencing,
making your code ways more terse and readable if you do it right. If you
have finished this, have a look at Boost.org's operators library
<http://www.boost.org/libs/utility/operators.htm> to see how some parts
can be even more generalised and for further advice on how to implement
correct and efficient operator overloads.

Regards, Daniel

--
Daniel Frey

aixigo AG - financial solutions & technology
Schloß-Rahe-Straße 15, 52072 Aachen, Germany
fon: +49 (0)241 936737-42, fax: +49 (0)241 936737-99
eMail: (e-mail address removed), web: http://www.aixigo.de

The hacks that we write today become the bugs of tomorrow.
 
M

Maxim Yegorushkin

gorda said:
The structure is simple: the base class has two int memebers "dataA",
"dataB". The derived class has an additional int member "dataC". I am
simply trying to overload the + operator so that 'adding' two objects
will sum up the corresponding int members.

Can someone tell me if I'm overloading the + operator correctly in the
derived class, or if there is a better way to do it?

The usual advise advocated in majority of books is to overload operator+= as a member function and then overload operator+ as non member function in terms of operator+=. It looks to me that the code will be simpler if you do it that way:
#include <iostream>
using namespace std;

class base
{
public:
//base int members
int dataA,dataB;

//default constructor
base():dataA(0),dataB(0)
{}
//overloaded constructor
base(int x, int y): dataA(x), dataB(y)
{}

base& operator+=(const base& rhs)
{
this->dataA += rhs.dataA;
this->dataB += rhs.dataB;
return *this;
}

and then:

inline
base operator+(base const& a, base const& b)
{
return base(a) += b;
}

class derived: public base
{
public:
//additonal derived class int member
int dataC;

//default constructor
derived():base()
{
dataC=0;
}

//overloaded constructor
derived(int x, int y, int z):base(x,y)
{
dataC=z;
}

//copy constructor
derived(const derived& t): base(t)
{
this->dataC = t.dataC;
}
derived operator+=(const derived &rhs)
{
this->base::eek:perator+=(rhs);
this->dataC += rhs.dataC;
return *this;
}
};

and implement derived operator+(derived const&, derived const&) in the same way as for base.

And there is a little more convenient approach which requires less typing - use boost operators library. Here it implements operator+ for you provided you implement operator+= (read the library docs for details). The code will look like this:

class base : public boost::addable1<base>
{
public:
base& operator+=(base const&);
};

class derived : public boost::addable1<derived, base> // derivation chain: derived -> addable1<derived, base> -> base -> addable1<base>
{
public:
derived& operator+=(derived const&);
};
 
A

Andrei Alexandrescu \(See Website for Email\)

Daniel Frey said:
I suggest you think about implementing operator+= instead of operator+.
After you have done that, implement operator+ in terms of operator+=.
It's a nice way of reducing some of the complexity you are experiencing,
making your code ways more terse and readable if you do it right. If you
have finished this, have a look at Boost.org's operators library
<http://www.boost.org/libs/utility/operators.htm> to see how some parts
can be even more generalised and for further advice on how to implement
correct and efficient operator overloads.

By the way, I was looking at
http://www.boost.org/libs/utility/operators.htm#symmetry and found the
following remark:

<<
The difference to the first implementation is that lhs is not taken as a
constant reference used to create a copy; instead, lhs is a by-value
parameter, thus it is already the copy needed. This allows another
optimization (12.2/2) for some cases. Consider a + b + c where the result of
a + b is not copied when used as lhs when adding c. This is more efficient
than the original code, but not as efficient as a compiler using the NRVO.
In what way(s) would the NRVO compiler generate better code? I understand
that the statement:

a + b + c;

which compiles to:

operator+(operator+(a, b), c);

will generate one temporary for operator+(a, b), which then... well, here's
the question - will that be copied again, and why? I know the boost folks
have run lots of tests, so I'd be curious on two things:

(1) In what ways does the NRVO compiler generates better code for the
NRVO-friendly code, than the URVO compiler for the URVO-friendly code?

(2) Why the NRVO compiler can't generate as good code for the URVO-friendly
code, as for the NRVO-friendly code. Is it a matter of principle, or
something that was just noticed?


Thanks!

Andrei
 
J

John Potter

In what way(s) would the NRVO compiler generate better code?
I understand that the statement:
a + b + c;
which compiles to:
operator+(operator+(a, b), c);
will generate one temporary for operator+(a, b), which then... well, here's
the question - will that be copied again, and why?

Yes. Because. Try generalizing the optimization. It is not easy.
(1) In what ways does the NRVO compiler generates better code for the
NRVO-friendly code, than the URVO compiler for the URVO-friendly code?

We can reduce it to a + b to see.

I operator+ (I const& lhs, I const& rhs) {
return I(lhs) += rhs;
}

The copy of lhs is required because it will be modified. The copy to
the return value is required because nothing requires += to return
*this and it is difficult/impossible to determine. Two copies.

I operator+ (I lhs, I const& rhs) {
lhs += rhs;
return lhs;
}

The copy to lhs is required because it will be modified. The copy to
the return value is required because the standard provides no way to
remove it. Two copies.

I operator+ (I const& lhs, I const& rhs) {
I rv(lhs);
rv += rhs;
return rv;
}

The NRVO merges the return value and the local. The copy to rv is
required because it will be modified. No copy to the return value
is required because it has been merged with the local. One copy.
If NRVO is not implemented, it will be two copies like the first
version.

If we extend this to three items, the first version adds two more
copies and the other two each add one copy. In the second case,
the return value from the first addition is merged with the first
parameter to the second add. If there is no NRVO, the third case
will be the same as the first.
(2) Why the NRVO compiler can't generate as good code for the URVO-friendly
code, as for the NRVO-friendly code. Is it a matter of principle, or
something that was just noticed?

Note that the optimizations are localized in both cases. The savings
in the third are local to the operator+. The savings in the second
are local to the calling code. In both cases, the function and calling
code could be in different translation units.

The first version uses two copies per operation. The second version
uses one copy per operation plus one copy. The third version uses one
copy per operation with NRVO and two without.

Removing the extra temporaries for additional operations in one
expression requires a much larger picture.

Of course, efficient code would simply not use operator+.

((d = a) += b) += c;

On three lines, if you like. Maybe the reason that implementers are
not spending great time on the global optimization? Or, are they?

John
 
S

Sandeep

Hi,

I am more puzzled by the above discussion regarding working of
overloaded operators + & +=.
If we say A += B, it means that A will change, B will not.
But in case of C = A + B, neither A and nor B is changing, both remain
as const.

Then how the following is justified?

base operator+ (base lhs, base const & rhs)
{
return lhs += rhs;
}
derived operator+ (derived lhs, derived const & rhs)
{
return lhs += rhs;
}

Regards
Sandeep
 
K

Karl Heinz Buchegger

Sandeep said:
Hi,

I am more puzzled by the above discussion regarding working of
overloaded operators + & +=.
If we say A += B, it means that A will change, B will not.
But in case of C = A + B, neither A and nor B is changing, both remain
as const.

Then how the following is justified?

base operator+ (base lhs, base const & rhs)
{
return lhs += rhs;
}
derived operator+ (derived lhs, derived const & rhs)
{
return lhs += rhs;
}

It isn't justified. It is plain wrong.
Where did you see this?

The key point is: no matter what you do, operator+ *has* to return
a new object. So you *can* do:

create that new object and initialize it with eg. lhs.
then use operator+= on that new object to add in the rhs

base operator+( base lhs, base const& rhs )
{
base result( lhs );
return result += rhs;
}

Or are you talking about a different topic? The wording of 'base' and
'derived' makes me think so, but I cannot figure out what that could
be.
 
F

Francis Glassborow

Sandeep said:
Hi,

I am more puzzled by the above discussion regarding working of
overloaded operators + & +=.
If we say A += B, it means that A will change, B will not.
But in case of C = A + B, neither A and nor B is changing, both remain
as const.

Then how the following is justified?

base operator+ (base lhs, base const & rhs)
{
return lhs += rhs;
derived operator+ (derived lhs, derived const & rhs)
{
return lhs += rhs;

Look at the parameters (and the return value.) lhs is a purely local
variable (because it is initialised by value).
 
K

Karl Heinz Buchegger

Karl said:
It isn't justified. It is plain wrong.

I am wrong.
Francis brought me on the track. lhs already is a copy of the callers
value

Sorry for the confusion
 
J

John Potter

I am more puzzled by the above discussion regarding working of
overloaded operators + & +=.
If we say A += B, it means that A will change, B will not.
But in case of C = A + B, neither A and nor B is changing, both remain
as const.
Then how the following is justified?
base operator+ (base lhs, base const & rhs)
{
return lhs += rhs;
}

Note that lhs is by value not reference. In C = A + B, lhs is a copy
of A which is modified by += with B and then copied to C.
derived operator+ (derived lhs, derived const & rhs)
{
return lhs += rhs;
}

Polymorphism usually makes no sense with these operators. Do you have
an example where it does?

John
 
J

John Potter

It isn't justified. It is plain wrong.
Hardly.

Where did you see this?

Maybe in the boost implementation?
The key point is: no matter what you do, operator+ *has* to return
a new object.

The above does.
So you *can* do:

create that new object and initialize it with eg. lhs.
then use operator+= on that new object to add in the rhs

The above lhs is by value producing the copy needed.
base operator+( base lhs, base const& rhs )
{
base result( lhs );
return result += rhs;
}

This is one naive inefficient version of operator+. It could
also be written in one line as

return base(lhs) += rhs;

The above version is more efficient when used more than once
in an expression. If the compiler implements NRVO, the
following is more efficient. If not, the above version is
more efficient.

base result( lhs );
result += rhs;
return result;

Your version does not allow NRVO in the function nor URVO in
the calling expression.
Or are you talking about a different topic? The wording of 'base' and
'derived' makes me think so, but I cannot figure out what that could
be.

Agreed. Operator+ and polymorphism do not mix well.

John
 
V

vze2hb3b

In comp.lang.c++.moderated Karl Heinz Buchegger said:
It isn't justified. It is plain wrong.

It is not!
Where did you see this?

The key point is: no matter what you do, operator+ *has* to return
a new object. So you *can* do:

create that new object and initialize it with eg. lhs.
then use operator+= on that new object to add in the rhs

Note that he passes lhs by value, so lhs is the new object. You
do not need to copy it again.
 
F

Francis Glassborow

Karl Heinz Buchegger said:
It isn't justified. It is plain wrong.
Where did you see this?

Look at the code more carefully, lhs is a value parameter and rhs is a
const reference one. The result is returned by value. Where is the
problem?
 
R

Randy Maddox

Karl Heinz Buchegger said:
It isn't justified. It is plain wrong.
Where did you see this?

The key point is: no matter what you do, operator+ *has* to return
a new object. So you *can* do:

create that new object and initialize it with eg. lhs.
then use operator+= on that new object to add in the rhs

base operator+( base lhs, base const& rhs )
{
base result( lhs );
return result += rhs;
}

Or are you talking about a different topic? The wording of 'base' and
'derived' makes me think so, but I cannot figure out what that could
be.

See John Potter's July 27 post in this thread for an excellent explanation.

Thanks, John.

Randy.
 
K

Karl Heinz Buchegger

Francis said:
Look at the code more carefully, lhs is a value parameter and rhs is a
const reference one. The result is returned by value. Where is the
problem?

In my bad habit of having the first parameter always a const reference.
I read something that was not written. :)
 

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,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top