Методы переопределения C ++ - PullRequest
7 голосов
/ 05 августа 2009

Я не могу понять, что с этим.

У меня есть класс Scene, который имеет вектор сущностей и позволяет вам добавлять и получать сущности со сцены:

class Scene {
    private:
        // -- PRIVATE DATA ------
        vector<Entity> entityList;
    public:
        // -- STRUCTORS ---------
        Scene();
        // -- PUBLIC METHODS ----
        void addEntity(Entity); // Add entity to list
        Entity getEntity(int); // Get entity from list
        int entityCount();
};

Мой класс сущностей выглядит следующим образом (вывод для тестирования):

class Entity {
    public:
        virtual void draw() { cout << "No" << endl; };
};

А потом у меня есть класс Polygon, который наследуется от Entity:

class Polygon: public Entity
{
    private:
        // -- PRIVATE DATA ------
        vector<Point2D> vertexList; // List of vertices
    public:
        // -- STRUCTORS ---------
        Polygon() {}; // Default constructor
        Polygon(vector<Point2D>); // Declare polygon by points
        // -- PUBLIC METHODS ----
        int vertexCount(); // Return number of vertices
        void addVertex(Point2D); // Add vertex
        void draw() { cout << "Yes" << endl; }; // Draw polygon
        // -- ACCESSORS ---------
        Point2D getVertex(int); // Return vertex
};

Как видите, у него есть метод draw (), который должен переопределять метод draw (), который он наследует от класса Entity.

Но это не так. При использовании следующего кода:

scene->getEntity(0).draw();

где сущность 0 является полигоном (или, по крайней мере, должен быть), он выводит «Нет» из родительского метода (как будто это не полигон, а просто сущность). На самом деле, это не позволяет мне вызывать любые методы, уникальные для Polygon, без получения:

' имя некоторого метода ': не является членом 'Entity'

Так есть идеи, что случилось?

Спасибо за помощь.

UPDATE:

Итак, я реализовал код, указанный в первом ответе, но я не уверен, как добавить свой многоугольник в список. Как то так?

const tr1::shared_ptr<Entity>& poly = new Polygon;
poly->addVertex(Point2D(100,100));
poly->addVertex(Point2D(100,200));
poly->addVertex(Point2D(200,200));
poly->addVertex(Point2D(200,100));
scene->addEntity(poly);

Я просто не привык к этому делу shared_ptr.

Ответы [ 5 ]

14 голосов
/ 05 августа 2009

Я думаю, что вам нужно опубликовать свой код вызова, но суть проблемы заключается в следующем.

У вас есть конкретный класс Polygon, производный от другого конкретного класса Entity. Ваши функции addEntity и getEntity принимают и возвращают Entity по значению , поэтому, если вы попытаетесь передать или получить Entity, вы скопируете только часть Entity этого объекта (разрезая его) и информация о производной части объекта будет потеряна.

Кроме того, у вас есть vector из Entity, который является вектором объектов базового класса, поэтому вы не можете хранить ничего, кроме базового типа объекта.

Если вам нужна коллекция объектов смешанного типа, но все они получены из Entity, вам может понадобиться использовать динамически создаваемые объекты и некоторый интеллектуальный указатель, такой как tr1::shared_ptr или boost::shared_ptr .

1021 * Е.Г. *

class Scene {
    private:
        // -- PRIVATE DATA ------
        vector< std::tr1::shared_ptr<Entity> > entityList;
    public:
        // -- STRUCTORS ---------
        Scene();
        // -- PUBLIC METHODS ----
        void addEntity( const std::tr1::shared_ptr<Entity>& ); // Add entity to list
        const std::tr1::shared_ptr<Entity> getEntity(int); // Get entity from list
        int entityCount();
};

Редактировать

Ваш обновленный код вызова в основном корректен, хотя использование локальной константной ссылки на общий указатель немного неясно.

Я бы, наверное, пошел с чем-то вроде:

std::tr1::shared_ptr<Polygon> poly( new Polygon );
poly->addVertex(Point2D(100,100));
poly->addVertex(Point2D(100,200));
poly->addVertex(Point2D(200,200));
poly->addVertex(Point2D(200,100));
scene->addEntity(poly);
1 голос
/ 05 августа 2009

Для этого вы должны использовать чисто виртуальную функцию.

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

Затем вызовите функцию рисования для вашего объекта, и вы также должны использовать указатели на свои объекты.

1 голос
/ 05 августа 2009

Комментарий Холлиды верен: вы помещаете объект типа Polygon в область памяти, предназначенную для типов Entity, и сталкиваетесь с тем, что называется slicing . Дополнительная информация «Полигон» удаляется, и все, что у вас остается, - это сущность.

Вы должны хранить указатели (или ссылки, если возможно) на базовые классы в этих ситуациях.

0 голосов
/ 05 августа 2009

Как правило, вы всегда должны использовать семантику ссылок (то есть доступ к объектам через указатели или ссылки) вместо семантики значений при работе с объектами, которые вы намереваетесь использовать полиморфно.

Чтобы обеспечить безопасность таким образом, целесообразно сделать базовый класс для всех ваших полиморфных типов некопируемым , создав частный конструктор копирования и оператор присваивания. Это эффективно предотвратит нарезку, поскольку код просто не сможет скомпилироваться, если семантика значения используется по ошибке.

0 голосов
/ 05 августа 2009

Вы должны хранить указатели (предпочтительно умные указатели :)) для экземпляров сущностей. Вектор перераспределяется при вставке, поэтому ваши объекты нарезаются еще до вызова метода получения.

Тип возвращаемого значения для метода-получателя также должен быть указателем или ссылкой, чтобы вы могли сделать этот полиморфный вызов.

...