operator overloading and inheritance help

W

woosu

Hello ladies and gentlemen.

I have a relatively simple problem that I've been unable to solve.

In the interest of learning C++, I've decided to write a simple game.
The basis for the game is 8x8 board (think chess). I decided to
implement iterators so it'd be easy to analyze the board position etc.

Basically I have the following hierachy:

board_iterator - generic iterator that walks the board left to right as
| if it was a vector (which it is)
|_ horizontal_iterator - an iterator that walks rows only
|_ veftical_iterator - an iterator that walks columns only
|_ diagonal_iterator - an iterator that can walk diagonals (either
left to right or right to left).


So here's the problem. I want to overload (for example) operator+(). As
far as I understand, operator+() returns a new instance of an object.


So, to put this in code:


class board_iterator
{
...
board_iterator operator+(int foo)
{
board_iterator new = *this; // <--- problem.
new.plus(foo);
return new;
}

virtual void plus(...)
{
// do some work that's board_iterator specific
}
...

};

class board_iterator_vertical: public board_operator
{
virtual void plus(...)
{
// do some work that's board_iterator_vertical specific
}
};



So lets say I want to do something like this:

board_iterator i,j,k;
i = j + 3;

This works fine.


But another scenario will not:

board_iterator i,j,k;
board_iterator_vertical a,b,c;
a = i + 3;

This will not work. board_iterator_vertical inherits operator+()
from board_iterator which returns board_iterator on operator+() instead
of board_iterator_vertical. How do I get around this? The key here is
that all operator+() will do the same:
1. create a new object (of proper type!! this is the key!)
2. run virtual plus() on it (which will be overloaded for all of
the
types).
3. Return it.

So how do I fix this? Obvious solution is to have operator+() for every
inherited class, instead of having operator+() call plus() but that
seems wrong somehow. In particular, the only difference between all of
the operator+() methods will be the way that next location is
calculated. It's not crucial in the long run, but I want to understand
what I'm doing wrong. I also want to do this almost-right.


Thoughts and opinions?

Thanks.
 
P

Phil Endecott

woosu said:
board_iterator operator+(int foo)
{
board_iterator new = *this; // <--- problem.
new.plus(foo);
return new;
}

virtual void plus(...)
{
// do some work that's board_iterator specific
}
This will not work. board_iterator_vertical inherits operator+()
from board_iterator which returns board_iterator on operator+() instead
of board_iterator_vertical. How do I get around this?

You need a clone() virtual method, implemented in each subclass, that
creates an new object of the appropriate class. This is sometimes
called a "virtual constructor". See section 20.8 of the FAQ.


--Phil.
 
V

Victor Bazarov

woosu said:
I have a relatively simple problem that I've been unable to solve.

In the interest of learning C++, I've decided to write a simple game.
The basis for the game is 8x8 board (think chess). I decided to
implement iterators so it'd be easy to analyze the board position etc.

Basically I have the following hierachy:

board_iterator - generic iterator that walks the board left to right as
| if it was a vector (which it is)
|_ horizontal_iterator - an iterator that walks rows only
|_ veftical_iterator - an iterator that walks columns only
|_ diagonal_iterator - an iterator that can walk diagonals (either
left to right or right to left).


So here's the problem. I want to overload (for example) operator+(). As
far as I understand, operator+() returns a new instance of an object.


So, to put this in code:


class board_iterator
{
...

board_iterator* clone() const;
board_iterator operator+(int foo)
{
board_iterator new = *this; // <--- problem.

(a) 'new' is a reserved word, you can't use it to name variables.

(b) If you just create an instance of 'board_iterator', you get what
is known as "slicing", where the origins of the object are lost,
and you get only the data related to the base class. What you need
here is to use a virtual member 'clone' which will return a pointer
to an object which is the same as the object for which it's called.

board_iterator *pbi = this->clone();
new.plus(foo);
pbi->plus(foo);

return new;

I am not sure about this, though. Might still be OK:

return *pbi;
}

virtual void plus(...)
{
// do some work that's board_iterator specific
}
...

};

class board_iterator_vertical: public board_operator
{
virtual void plus(...)
{
// do some work that's board_iterator_vertical specific
}
};



So lets say I want to do something like this:

board_iterator i,j,k;
i = j + 3;

This works fine.


But another scenario will not:

board_iterator i,j,k;
board_iterator_vertical a,b,c;
a = i + 3;

This will not work. board_iterator_vertical inherits operator+()
from board_iterator which returns board_iterator on operator+() instead
of board_iterator_vertical. How do I get around this? The key here is
that all operator+() will do the same:
1. create a new object (of proper type!! this is the key!)

See the 'clone' suggestion above.
2. run virtual plus() on it (which will be overloaded for all of
the
types).
3. Return it.

So how do I fix this? Obvious solution is to have operator+() for every
inherited class, instead of having operator+() call plus() but that
seems wrong somehow. In particular, the only difference between all of
the operator+() methods will be the way that next location is
calculated. It's not crucial in the long run, but I want to understand
what I'm doing wrong. I also want to do this almost-right.


Thoughts and opinions?

The main thing is that your 'board_iterator_vertical' and all others
should have constructors from 'board_iterator' (along with the member
functions I added).

You need to explain what happens if I do

bi i;
bi_v iv = i + 5;
bi_h ih1 = iv + 3, ih2 = i + 3;

Notice the different types. Is it allowed? If you provide constructors
from 'board_iterator' in all derived types, it would be possible, but does
it make sense?

V
 
W

woosu

Victor said:
(a) 'new' is a reserved word, you can't use it to name variables.

My mistake, but this was for the purposes of demonstration.

See the 'clone' suggestion above.

Thanks. I should've been able to come up with this myself.
Phil, thanks a bunch. I've scoured the FAQ before I posted,
but couldn't find it.
You need to explain what happens if I do

bi i;
bi_v iv = i + 5;
bi_h ih1 = iv + 3, ih2 = i + 3;

Notice the different types. Is it allowed? If you provide constructors
from 'board_iterator' in all derived types, it would be possible, but does
it make sense?

I dont see why not. Iterator is simply a location on the board. The
only difference is where operator+() or operator-() will take it.

bi i; // initialize general iterator to start (location = 0);
bi_v iv = i + 5; // vertical iterator = location of i (0) + 5 = 5
bi_h ihl = iv +3; // horiz. iterator = location of iv + 3 = 29
// (from location 5, 3 rows down,
// 5+3*8 = 29)


Does that make sense?

Thanks again.

-d
 
V

Victor Bazarov

woosu said:
[..]
You need to explain what happens if I do

bi i;
bi_v iv = i + 5;
bi_h ih1 = iv + 3, ih2 = i + 3;

Notice the different types. Is it allowed? If you provide constructors
from 'board_iterator' in all derived types, it would be possible, but does
it make sense?


I dont see why not. Iterator is simply a location on the board. The
only difference is where operator+() or operator-() will take it.

bi i; // initialize general iterator to start (location = 0);
bi_v iv = i + 5; // vertical iterator = location of i (0) + 5 = 5
bi_h ihl = iv +3; // horiz. iterator = location of iv + 3 = 29
// (from location 5, 3 rows down,
// 5+3*8 = 29)


Does that make sense?

I wasn't contending that it doesn't. I was merely asking.

As Phlip will undoubtedly tell you, work out good test cases for moving
and converting all those iterators and write them and any time you do
something to change the iterator behaviour, the tests should still both
compile and execute correctly.

V
 
W

woosu

Victor said:
...
As Phlip will undoubtedly tell you, work out good test cases for moving
and converting all those iterators and write them and any time you do
something to change the iterator behaviour, the tests should still both
compile and execute correctly.

V

Fair enough and thanks for help.

One quick question though. In your first post, you first allocate *pbi
via this->clone() (which creates a new object via new()). Then you
return *pbi.

The object is returned by value (return *pbi), which means a copy is
created. How do I take care of the memory allocated for pbi? The FAQ
deletes the memory at the end of the function call, but that doesn't
work in my case.

What do I do?

(I hope I'm not missing anything).

-d
 
V

Victor Bazarov

woosu said:
Fair enough and thanks for help.

One quick question though. In your first post, you first allocate *pbi
via this->clone() (which creates a new object via new()). Then you
return *pbi.

The object is returned by value (return *pbi), which means a copy is
created. How do I take care of the memory allocated for pbi? The FAQ
deletes the memory at the end of the function call, but that doesn't
work in my case.

What do I do?

You're right. It's a memory leak. You can wrap the 'pbi' into a smart
pointer. 'return' should create a copy and after that the original object
(owned by the smart pointer) should be disposed of.

V
 

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,011
Latest member
AjaUqq1950

Latest Threads

Top