Наследование в C ++ - PullRequest
       15

Наследование в C ++

6 голосов
/ 26 октября 2008

Вот моя проблема: у меня есть виртуальный метод, определенный в файле .h, который я хочу вызвать в классе, который наследуется от базового класса. К сожалению, метод в производном классе не вызывается. Есть ли лучший способ реализовать то, что я пытаюсь сделать?

#ifndef ofxBASE_SND_OBJ
#define ofxBASE_SND_OBJ

#include "ofConstants.h"

class ofxBaseSndObj {

public:

    virtual string getType(){}

    string key;

};

#endif

Вот мой класс гудка

#ifndef OFXSO_BUZZ
#define OFXSO_BUZZ

#include "ofxBaseSndObj.h"

class ofxSOBuzz : public ofxBaseSndObj
{
public:
    string getType();
};

#endif

ofxSOBuzz.cpp

string ofxSOBuzz::getType()
{
    string s = string("ofxSOBuzz");
    printf(" ********* returning string type %s", s.c_str()); // doesn't get called!
    return s;
}

Затем в другом классе я пытаюсь назвать это так:

string ofxSndObj::createFilter(ofxBaseSndObj obj)
{
    string str = obj.getType();
    if(str.compare("ofxSOBuzz") == 0)
    {
        printf(" all is well ");
    }
}

В приведенном выше методе мне нужно иметь возможность передавать один из множества видов объектов, которые расширяют объект ofxBaseSndObj. Любые предложения или указатели будут с благодарностью. Спасибо!

Ответы [ 6 ]

25 голосов
/ 26 октября 2008

Изменить эту строку:

string ofxSndObj::createFilter(ofxBaseSndObj obj)

до

string ofxSndObj::createFilter(ofxBaseSndObj& obj)

То, что вы делаете, это передача по значению (передача копии).

Это означает, что вы копируете объект в функцию. Поскольку функция не знает, какой тип вы фактически передаете, она передает только тип, определенный в объявлении функции, и, таким образом, она создает копию базового класса (это известно как проблема slicing ).

Решение - передать по ссылке.

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

class ofxBaseSndObj
{
    public:
        virtual string getType()  const;
        // If the method does not change the object mark it const

        string key;

};

string ofxSndObj::createFilter(ofxBaseSndObj const& obj)
{
    // allowed to call this if getType() is a const
    string str = obj.getType();

    if(str.compare("ofxSOBuzz") == 0)
    {
        printf(" all is well ");
    }
}
10 голосов
/ 26 октября 2008

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

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

Если вы сделали его абстрактным методом, то компилятор жаловался бы на попытку создания экземпляра абстрактного класса в вашем методе ofxSndObj :: createFilter.

2 голосов
/ 26 октября 2008

Создание конструктора копирования и оператора = private - эффективный способ предотвратить повторение этой ошибки.

Например:

class ofxBaseSndObj {
public:
    virtual string getType(){}
    string key;

private:
    ofxBaseSndObj(const ofxBaseSndObj& rhs);
    ofxBaseSndObj& operator=(const ofxBaseSndObj& rhs);
};

Если нет других веских причин, вы должны использовать встроенный в CTI RTTI. Затем вы можете использовать оператор typeid. Посмотрите на документацию вашего компилятора, чтобы включить его, если он не включен по умолчанию.

2 голосов
/ 26 октября 2008

Эта проблема называется "нарезка" в C ++.

1 голос
/ 26 октября 2008

Другие решили проблему нарезки. Затем вы спрашиваете Хорошо, позвольте мне сказать, я знаю, что мне нужно что-то сделать для определения базового типа, но есть ли что-то более элегантное, чем поиск по enum для определения типа унаследованного объекта? Запросы и включение типа объекта - это плохой дизайн, в котором упущен смысл ОО-подхода.

Вместо

string ofxSndObj::createFilter(ofxBaseSndObj& obj)
{
    string str = obj.getType();
    if(str.compare("ofxSOBuzz") == 0)
    {
        // do ofxSOBuzz - specific thing
    }
    else if(str.compare("some other derived class") == 0)
    {
        // do stuff for other derived classes
    }
       // etc...
}

сделайте интересное поведение виртуальной функцией:

class ofxBaseSndObj {

public:
    // get rid of getType()
    virtual void HelpCreateFilter() = 0;
};


string ofxSndObj::createFilter(ofxBaseSndObj& obj)
{
    // Let the derived class do it's own specialized work.
    // This function doesn't need to know what it is.
    obj.HelpCreateFilter();
    // rest of filter creation
}

Почему это лучше, чем оригинальная версия? Потому что ofxSndObj::createFilter не нуждается в модификации, если будущие производные классы ofxBaseSndObj будут добавлены в систему. Ваша версия нуждается в расширении для каждого нового производного класса. Если это неясно, попробуйте опубликовать немного больше кода - я не могу сказать из вашего кода или имен классов, что эти функции должны делать.

0 голосов
/ 26 октября 2008

Вы можете использовать dynamic_cast или type_id

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...