17

Shape.h

namespace Graphics {
    class Shape {
    public:
        virtual void Render(Point point) {};
    };
}

Rect.h

namespace Graphics {
    class Rect : public Shape {
    public:
        Rect(float x, float y);
        Rect();
        void setSize(float x, float y);
        virtual void Render(Point point);

    private:
        float sizeX;
        float sizeY;
    };
}

struct ShapePointPair {
    Shape shape;
    Point location;
};

Used like this:

std::vector<Graphics::ShapePointPair> theShapes = theSurface.getList();

for(int i = 0; i < theShapes.size(); i++) {
    theShapes[i].shape.Render(theShapes[i].location);
}

This code ends up calling Shape::Render and not Rect::Render

I'm assuming this is because it is casting the Rect to a Shape, but I don't have any idea how to stop it doing this. I'm trying to let each shape control how it is rendered by overriding the Render method.

Any ideas on how to achieve this?

dedObed
  • 1,313
  • 1
  • 11
  • 19
Simon Moles
  • 359
  • 1
  • 5
  • 11
  • Maybe you want to show us the code that creates the vector elements? – Frank Bollack Sep 18 '09 at 11:45
  • 1
    The problem and solution are almost identical to this http://stackoverflow.com/questions/1230006/ question. As there, you have a vector of (struct containting a) concrete base class, which you must be creating by *slicing* derived classes if you expecting Rect::Render to be called. – CB Bailey Sep 18 '09 at 11:53

8 Answers8

35

Here's your problem:

struct ShapePointPair {
        Shape shape;
        Point location;
};

You are storing a Shape. You should be storing a Shape *, or a shared_ptr<Shape> or something. But not a Shape; C++ is not Java.

When you assign a Rect to the Shape, only the Shape part is being copied (this is object slicing).

dave4420
  • 46,404
  • 6
  • 118
  • 152
  • auto_ptr will loose data as the ShapePointPair is copied around in the vector – mmmmmm Sep 18 '09 at 11:50
  • 1
    Thanks. I'd been hoping to do it without pointers since the shapes don't use much memory so are simple to copy around on the stack, but it seems thats not possible. Thanks all for the quick replies. – Simon Moles Sep 18 '09 at 11:59
4

This problem is called slicing - you lose the derived functionality when copying to a base. To avoid this use pointers to the base class, i.e.

std::vector<Graphics::Shape*> s;
s.push_back(&some_rect);
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
2

The problem is that in your vector you are storing copies of Shape objects, and copying a Shape object does not copy the data or functionality of its derived classes - you're slicing the polymorphism away.

Manage the objects using new and delete, and arrange for your vector to store pointers to them.

moonshadow
  • 86,889
  • 7
  • 82
  • 122
1

The polymorphism will only work from a pointer to a shape, not from a shape object.

Locksfree
  • 2,682
  • 23
  • 19
1

You are accessing the shape object directly for the override to work you need to access the object via a pointer or references.

For example when you assigne the Shape into the ShapePointPair the code will 'slice' the object and only copy the Shape bit into the ShapePointPair

Doing this will mean you have to watch memory management - so you could use a smart pointer in the struct ShapePointPair { smart_pointer shape; Point location; };

mmmmmm
  • 32,227
  • 27
  • 88
  • 117
0

No, it is not casting.

You can instead store a reference to baseclass Point:

struct ShapePointPair {
        Shape shape;
        Point &location;
};

This reference must be set at construction time for struct ShapePointPair. Add a constructor to ShapePointPair for this purpose. It must be passed (newly created) instances of Rect.

Also observe the memory management responsiblities (proper written destructors, etc.).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0

You could try boost::ptr_vector

http://www.boost.org/doc/libs/1_40_0/libs/ptr_container/doc/ptr_container.html

Chris Bednarski
  • 3,364
  • 25
  • 33
0

I'm not sure to explain well because of my english is poor.

I think you should have to use it reference or pointer type. because shape is exactly defined what it has to do.

If you use your code directly, your child try to copy and do shape's working. That is why doesn't work your override function.

use pointer or reference like this.

pointer.h

class Parent {
public:
    virtual void work() { printf("parent is working now\n"); }
};
class Child1 {
public:
    virtual void work() { printf("child1 is working now\n"); }
};
class Child2 {
public:
    virtual void work() { printf("child2 is working now\n"); }
};
struct Holder {
    Parent* obj1;
    Parent* obj2;
};
int main() {
    Child1 child1;
    Child2 child2;
    Holder holder = { &child1, &child2 };
    holder.obj1->work();
    holder.obj2->work();
    return 0;
}

reference.h

class Parent {
public:
    virtual void work() { printf("parent is working now\n"); }
};
class Child1 {
public:
    virtual void work() { printf("child1 is working now\n"); }
};
class Child2 {
public:
    virtual void work() { printf("child2 is working now\n"); }
};
struct Holder {
    Parent& obj1;
    Parent& obj2;
};
int main() {
    Child1 child1;
    Child2 child2;
    Holder holder = { child1, child2 };
    holder.obj1.work();
    holder.obj2.work();
    return 0;
}

*ps: personally i use abstract function(virtual void something() = 0;). because i also forgot about it sometimes so i catch it as syntax error.

James
  • 1