Sign in to follow this  
MrPickle

Inheritance and iterators

Recommended Posts

I have 2 classes like so:
class Shape
{
public:
   int width, height;
   virtual int area() { return 0; }
};

class Square : public Shape
{
public:
   int area() { return width*height; }
};

Then I have a vector defined like so: std::vector<Shape*> shapes; I try and iterate through the vector like this:
for(std::vector<Shape*>::iterator it = shapes.begin(); it != shapes.end(); it++)
{
   Shape &s = **it;
   
   s.area();
}
The problem is that whenever I call area, even if s is a Square, it calls Shape's area function. I thought that the virtual keyword was so that the inheriting class can redefine the function? Could somebody please tell me why Shape's area function is called instead of Square's area function?

Share this post


Link to post
Share on other sites
Are you sure that's the code you're using? This sample using those classes verbatim works fine and calls Square::area:
int main()
{
std::vector<Shape*> v;

Shape* s = new Square;
v.push_back(s);

Shape& s2 = **v.begin();
s2.area();
}



It's possible that you don't really have a Square, even though you think you do...

EDIT: I should add that if you breakpoint in the debugger and watch s, the debugger should tell you what the actual type is when you expand the class.

Share this post


Link to post
Share on other sites
Oops, in my attempt to make an example without all of the excess I missed out what I figure's causing the problem.

I'm trying to overload Square::area() with one function taking one derived class from shape and the second taking another derived class from shape and I have defined the class Shape as taking a Shape. (if this makes sense)

do I have declare a virtual function for each overload?

Share this post


Link to post
Share on other sites
Functions are only called virtually when you use a pointer, so the line below can only call Shape members.

Shape &s = **it;

Should be

Shape* s = *it;

You should post your code

Share this post


Link to post
Share on other sites
Quote:
Original post by MrPickle
do I have declare a virtual function for each overload?
Each overload is a distinct and different function, and each of them needs to exist as a virtual in the base class, yes.
Quote:
Original post by taz0010
Functions are only called virtually when you use a pointer, so the line below can only call Shape members.
No! This is completely false. Calling a virtual function from a reference or a pointer will do the same thing.

xissburg posted the same false information earlier.

The only time slicing will occur is if you copy the object by value
Shape s = **it;//make a copy into a new shape object
s.area();//object has been sliced, this will always call Shape::area

Share this post


Link to post
Share on other sites
Quote:
No! This is completely false. Calling a virtual function from a reference or a pointer will do the same thing.


Interesting. Didn't realise this.

Quote:
The only time slicing will occur is if you copy the object by value


I'm also getting slicing when I copy using references. (Which is in effect a copy by value of course)


B* der = new D(1,2,3);
B* der2 = new D(4,5,6);
B& ref = *der;
B& ref2 = *der2;

ref = ref2;


This seems a little confusing. I think I'll stick to using pointers when inheritance is concerned.

Share this post


Link to post
Share on other sites
Quote:
Original post by taz0010
B* der = new D(1,2,3);
B* der2 = new D(4,5,6);
B& ref = *der;
B& ref2 = *der2;

ref = ref2;
Yeah, the references are like a "short-hand" for writing whatever they're bound do, so your example of "ref = ref2;" is the same as writing:
*der = *der2;
Which copies the value, not the addresses.

Share this post


Link to post
Share on other sites
Quote:
Original post by taz0010
Quote:
No! This is completely false. Calling a virtual function from a reference or a pointer will do the same thing.


Interesting. Didn't realise this.

Quote:
The only time slicing will occur is if you copy the object by value


I'm also getting slicing when I copy using references. (Which is in effect a copy by value of course)


B* der = new D(1,2,3);
B* der2 = new D(4,5,6);
B& ref = *der;
B& ref2 = *der2;

ref = ref2;


This seems a little confusing. I think I'll stick to using pointers when inheritance is concerned.


You would get the same slicing with: *der = *der2; which, strictly speaking, doesn't use any references. You may want to look into boost::noncopyable, or making your copy constructor and assignment operator private, to prevent this kind of blatant error, in the case of inheritance trees. It will work, no matter which you're using.

Share this post


Link to post
Share on other sites
Quote:
Original post by taz0010
I'm also getting slicing when I copy using references. (Which is in effect a copy by value of course)


The phrase "copy by value" doesn't really mean anything; copying is copying.

The point of a reference is to, well, refer to the referred-to thing. If you say to assign to the thing you're referring to, then that assignment takes place. A pointer points at the pointed-at thing; in effect, it's a separate entity whereas the reference is just another name for the value. So the difference in copying behaviour is exactly what should be expected and should not be confusing at all. It also happens to be very convenient in a lot of cases: it lets you write functionality with stronger guarantees (because a C++ reference can't legally be "null" or invalid), and avoids writing so much of what I call "*&*&ing syntax". :)

Quote:
I think I'll stick to using pointers when inheritance is concerned.


Ultimately, you will normally need to heap-allocate to make useful use of inheritance (but note that there are other ways to do polymorphism :) ), so you'll have pointers anyway. But you can at least wrap them up in smart pointer classes to avoid manual memory management. And writing something like 'X& x = **it;' is idiomatic, and lets you access members with . instead of ->.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this