Моя виртуальная функция не работает C ++ - PullRequest
4 голосов
/ 13 мая 2011

Я отредактировал это из своего реального кода, чтобы его было немного легче понять.

Базовый класс:

class MWTypes
{
public:
    virtual long get() { return (0); }
};

Производный класс: (Там собираютсябыть другими классами, такими как char, double и т. д. и т. д.)

class TypeLong : public MWTypes
{
public:
    TypeLong(long& ref) : m_long(ref) {}
    ~TypeLong();

    long get() { return m_long; }
private:
    long& m_long;
};

и класс хранения:

class RowSet
{
public:
    void addElememnt(MWTypes elem);
    MWTypes getElement();

    std::vector<MWTypes> getVector() { return m_row; }

private:
    std::vector<MWTypes> m_row; 
};

Как он называется:

for (i = 0; i < NumCols; i++) // NumCols is 3 on this instance
{
    switch(CTypeArray[i]) // this is an int which identifies the type
{  
    case SQL_INTEGER:
    {
    long _long = 0;

    TypeLong longObj(_long);
    MWTypes *ptr = &longObj;

            // some SQL code goes here that changes the value of _long, 
            // there is no need to include it, so this will do.
    _long++;

            // I now want to save the data in a vector to be returned to the user.
    rowSet.addElememnt(*ptr);   

///////////////////////////////////////////////
// some code happens here that is irrelevant //
///////////////////////////////////////////////

// I now want to return the typr I have saved in the vector, 
// I THINK I am doing this right?
    MWTypes returned = rowSet.getElement();

    // lastly I want to get the value in the returned type
    long foo = returned.get();

///////////////////////////////////////////////
// some code happens here that is irrelevant //
///////////////////////////////////////////////

Я думаю, что я на правильных линиях здесь.Значение 'foo' всегда равно 0. У меня такое ощущение, что это может быть способ сохранения Im в векторе или базовая виртуальная функция, поскольку она всегда возвращает 0.

Если я удаляювозвращаясь в базовый класс, я получаю ошибки LNK2001.

Ответы [ 6 ]

8 голосов
/ 13 мая 2011
 MWTypes returned = rowSet.getElement();

 // lastly I want to get the value in the returned type
 long foo = returned.get();

должно быть

 MWTypes* returned = &rowSet.getElement();

 // lastly I want to get the value in the returned type
 long foo = returned->get();

или

 MWTypes& returned = rowSet.getElement(); // actually illegal, but MSVC will let you do

 // lastly I want to get the value in the returned type
 long foo = returned.get();

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

РЕДАКТИРОВАТЬ: это не единственная ваша проблема.Тот факт, что вектор хранит объекты (а не указатели), будет разрезать объекты и уничтожать информацию о их типах.

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

3 голосов
/ 13 мая 2011

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

Не предоставляя реализацию функции"get", как показывает код ascanio (делая функцию "чисто виртуальной"), не позволит вам сделать эту ошибку копирования, потому что компилятор не позволит вам создать экземпляр класса MWTypes, если вы это сделаете (он скажет, что класс"абстрактные").

3 голосов
/ 13 мая 2011

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

Чтобы исправить это, вы можете хранить указатели на базу: std::vector<MWTypes*>, но тогда вам нужно правильно управлять своими экземплярами, чтобы избежать утечек памяти.

class RowSet
{
public:
    // addElement assumes responsibility for the memory allocated for each 'elem'
    void addElement(MWTypes* elem);
    MWTypes* getElement();

    std::vector<MWTypes*> getVector() { return m_row; }

    // Destructor calls delete on every pointer in m_row
    ~RowSet();
private:
    std::vector<MWTypes*> m_row; 
};

Затем вам нужно исправить свой код, который вызывает addElement () для создания new экземпляров и для возврата длинных значений:

rowSet.getElement()->get();
1 голос
/ 13 мая 2011

Ваша проблема заключается в этой функции void addElememnt(MWTypes elem);. Это должно быть либо void addElememnt(MWTypes* elem);, либо void addElememnt(MWTypes& elem);. Это связано с тем, что аргумент, передаваемый по значению, теряет свой полиморфизм. Передача по значению вызывает конструктор копирования базового класса и ТОЛЬКО копирует содержимое базового класса (и виртуальной таблицы), игнорируя остальное из производного класса.

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

0 голосов
/ 13 мая 2011
Все детали

vector, getElement и addElememnt вызывают нарезку объектов, поскольку они хранят базовый объект по значению. Вам нужно работать с указателями или ссылками, чтобы использовать полиморфизм во время выполнения.

В этом случае, вероятно, вам нужно либо boost::ptr_vector, либо vector из shared_ptr.

0 голосов
/ 13 мая 2011

Проблема заключается здесь:

class RowSet
{
public:
    void addElememnt(MWTypes elem);

Вы берете elem по значению, а не по указателю или по ссылке, поэтому подобъект TypeLong отсечен, здесь: (ссылка: Что такое проблема нарезки в C ++? )

TypeLong longObj(_long);
MWTypes *ptr = &longObj;
_long++;
rowSet.addElememnt(*ptr);   

Вам необходимо изменить addElement, чтобы получить ссылку или указатель.

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