n1ce
n1ce

Reputation: 23

Polymorphism in lists in C++

I'm programming a basic graphic scene editor with OpenGL and C++, and this is the context:

I'm having this issues:

I don't really like this so I want to ask if there is some method to copy the objects inside the add function and then store the copy in the list, which it's not a problem because the copied object it's going to erase soon. Inside that function I don't know which subclass of Drawable I'm attending. I can't make a copy directly of the Drawable object because Drawable is an abstract class, can't make Drawable objects with new for the same. Just want to have a list with different objects which all can execute draw(). I leaving some code just in case:

class Drawable {
public:
    virtual void draw() = 0;

    virtual ~Drawable() = 0 {}
};


class Figure : public Drawable {
private:
    list<Point, allocator<Point>> _points;
    int _type;

public:
    ...

    void draw() {
        ...
    }   
};


class Scene : public Drawable {
private:
    list<Drawable*, allocator<Drawable*>> _drawables;
    ...

public:
    ...

    void add(Drawable* drawable) {
        _drawables.push_back(drawable);
    }

    ~Scene() {
        for(iterDrawable it = _drawables.begin(); it != _drawables.end(); ++it)
        delete (*it);
    }

    void draw() {
        for(iterDrawable it = _drawables.begin(); it != _drawables.end(); ++it)
            (*it)->draw();
    }   
};


main.cpp
...
void display() {
    ...

    Figure* square = new Figure(GL_POLYGON);

    square->add(Point(xSquare, ySquare));
    square->add(Point(xSquare + squareWidth, ySquare));
    square->add(Point(xSquare + squareWidth, ySquare + squareHeight));
    square->add(Point(xSquare, ySquare + squareHeight));

    cScene.add(square);
    cScene.draw();

    ...
}
...

I hope I explained sufficiently.

Upvotes: 2

Views: 785

Answers (2)

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

  1. Drawable must have a virtual destructor.
  2. You must have some kind of object ownership policy in place. In other words, for each and every object you allocate, you must have a very clear idea about disposing of that object. That's how things are done in C++, as opposed to Java. When you allocate an object for adding to a scene, does the scene object become the ultimate owner of the component? If it does, then the scene has to delete the list of its components before it itself goes away (that is, in the destructor). If the scene object is not the owner, and merely a user, then who is the owner? Can you guarantee that the owner will outlive all users? If not, how the owner will pass ownership to the next owner? If you are going to copy an object, you must decide this question for the original and for the copy.
  3. Fancy stuff like shared_ptr and ptr_list are fine and dandy, but I suggest learning the basics first. It is important to understand why shared_ptr and ptr_list are needed, which problems they purport to solve, and what their solutions look like from 30000 feet. The best way to acquire such understanding is by stumbling upon, and trying to solve, some of these problems.
  4. Drawable must have a virtual destructor.
  5. If you want to copy objects polymorphically, then you need a polymorphic function that makes a copy. You have to write one yourself for each derived class, just like you write draw() for each derived class. The commonly accepted name for such a function is clone. For each class Foo, clone would look like this:

    virtual Foo* clone() {
        return new Foo(*this);
    }

    Versions of clone look almost identically for all classes, nevertheless you do have to write each one of them manually. Note that this is using Foo's copy constructor, which you will have to write yourself if Foo owns any other object, or has a copy operation that is non-trivial in some other way.
  6. Did I mention that Drawable must have a virtual destructor?

Upvotes: 1

Loki Astari
Loki Astari

Reputation: 264461

What you want to use is a boost ptr container:

boost::ptr_list<Drawable>   drawlables;

/// STUFF

drawlables.add(new Square);

The advantage of boost ptr containers (over a container of smart pointers) is that the container makes access to the elements look like objects (not pointers). This makes using them with the standard algorithms trivial.

The ptr containers take ownership of any pointer inserted into the container so memory mangement is covered. All other locations should be passed a reference from the container.

drawlables[0].draw();

// or drawl all of them
for_each(drawlables.begin(), drawlables.end(), std::mem_fun(&drawlables::draw));

Upvotes: 3

Related Questions