Путать с переопределением в C ++ - PullRequest
2 голосов
/ 01 ноября 2010

Я пытаюсь сделать класс наследуемым от другого и переопределить некоторые методы. Заголовок класса:

class Objeto {  
public:  
    virtual bool interseca(const Rayo &rayo, float magnitud);  
    virtual bool breakNormal(const Punto &punto);  
    virtual Vector normal(const Punto &punto);  

    int idMaterial;  
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca(const Rayo &rayo, float magnitud);
    // etc
};

Далее в другом месте программы (за пределами Objeto и Esfera) я делаю:

// ObjectList is a Vector<Objeto>
Objeto o = esfera; /* Where esfera is a valid Esfera object */
ObjectList[0] = o;
ObjectList[0].interseca(rayo, magnitud);

Я хочу назвать новую версию interseca, которая находится в Esfera. Таким образом, я могу добавить больше объектов (Куб, Треугольник и т. Д.) И рассматривать их как общие «Объекты».

Но вместо реализации Esfera interseca программа вызывает Objeto::interseca.

Как правильно сделать это переопределение с C ++? Это способ сделать переопределение, и я что-то упустил, или я совершенно не прав? Любой совет или альтернатива, чтобы сделать это?

Ответы [ 4 ]

7 голосов
/ 01 ноября 2010

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

Нарезка:

http://en.wikipedia.org/wiki/Object_slicing

С учетом этих определений:

#include<iostream>

class Objeto {  
public:  
    virtual bool interseca() {
        return false;
    }
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca() {
        return true;
    }
};

Это не будет делать то, что вы хотите:

Esfera e;
Objeto o = e;
std::cout << o.interseca() << "\n";

ложь

Но это будет:

Esfera e;
Objeto& o = e;
std::cout << o.interseca() << "\n";

правда

Разработка программы

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

Вы когда-нибудь могли бы создать истинное Objeto в своей сцене или вы просто определяете базовый тип? Если вы просто определяете базовый тип (который я рекомендую), тогда вы можете сделать interseca, breakNormal и normal чисто виртуальными . Затем компилятор обнаружит проблемы, подобные той, что у вас есть.

class Objeto {  
public:  
    virtual bool interseca() = 0;
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca()
    {
        return true;
    }
};

// ...

Тогда это будет хорошо:

Esfera e;
Objeto& o = e;
std::cout << o.interseca() << "\n";

компиляция завершена

Но это приведет к ошибке компилятора - хорошо, потому что он ловит ошибку:

Esfera e;
Objeto o = e;
std::cout << o.interseca() << "\n";

ошибка: невозможно выделить объект абстрактного типа 'Objeto'

3 голосов
/ 01 ноября 2010

Ваше переопределение правильное, но, похоже, вы еще не полностью поняли объектную модель C ++. Это

Objeto o = esfera;

не делает то, что вы думаете, что делает. Этот создает копию подобъекта Objeto esfera в o. (Это называется " slicing ".) Вместо этого вы хотите reference esfera, используя либо ссылку

Objeto& o = esfera; // note the & 

или указатель o

Objeto* o = &esfera;

В случае сомнений всегда предпочитайте ссылки .

2 голосов
/ 01 ноября 2010

Вы успешно переопределили метод. Проблема заключается в том, как вы храните объекты и в том, как достигается полиморфное поведение в C ++.

Чтобы получить полиморфное поведение, вы должны ссылаться на объект с помощью указателя или ссылки. C ++ не поддерживает семантику ссылочных типов так же, как Java или C #. Если у вас есть переменная, созданная следующим образом

Objeto o = esfera;

переменная o - это новый объект (статический и динамический) типа Objeto, созданный на основе объекта esfera. То, что здесь происходит, называется нарезкой объектов .

Чтобы получить полиморфное поведение, вы должны использовать указатель или ссылку. Например, предположим, что переменные objeto и esfera имеют типы Objeto и Esfera соответственно.

Objeto* op = &objeto;
op->interseca(rayo, magnitud); // virtual dispatch is used but since op points
                               // to an object of type Objeto Objeto::interseca
                               // gets called.
op = &esfera;
op->interseca(rayo, magnitud); // virtual dispatch is used and this time
                               // Esfera::interseca gets called.

Вы не можете хранить объекты разных типов в одном vector. Если вы создадите vector<Objeto> и попытаетесь сохранить в нем объект типа Esfera, то нарезка объектов будет происходить, как в примере с переменной o.

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

0 голосов
/ 01 ноября 2010

Вы создали Objeto в стеке.Этот предмет всегда имеет тип Objeto.Для полиморфизма вы должны использовать ссылку или указатель.

...