C ++ - виртуальные деструкторы и ошибки компоновщика - PullRequest
4 голосов
/ 23 декабря 2011

У меня есть этот интерфейс, который я написал:

#ifndef _I_LOG_H
#define _I_LOG_H

class ILog {
public:
    ILog();
    virtual ~ILog();

    virtual void LogInfo(const char* msg, ...) = 0;
    virtual void LogDebug(const char* msg, ...) = 0;
    virtual void LogWarn(const char* msg, ...) = 0;
    virtual void LogError(const char* msg, ...) = 0;

private: 
    Monkey* monkey;
};

#endif

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

Undefined reference to ILog::ILog
Undefined reference to ILog::~ILog

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

РЕДАКТИРОВАТЬ: Хорошо, поэтому мне нужно также определить виртуальный деструктор. Но могу ли я по-прежнему выполнять вещи в определении виртуального деструктора, или он просто вызовет мой деструктор производных классов и пропустит его? Мол, сработает ли это:

virtual ~ILog() { delete monkey; }

Ответы [ 3 ]

10 голосов
/ 23 декабря 2011

Вы не определили конструктор и деструктор, вы только объявили их

Попробуйте

class ILog {
public:
    //note, I want the compiler-generated default constructor, so I don't write one
    virtual ~ILog(){} //empty body

    virtual void LogInfo(const char* msg, ...) = 0;
    virtual void LogDebug(const char* msg, ...) = 0;
    virtual void LogWarn(const char* msg, ...) = 0;
    virtual void LogError(const char* msg, ...) = 0;
};
  • Конструктор: Как только вы объявляете конструктор, любой конструктор, компилятор не генерирует конструктор по умолчанию для вас. Конструктор производного класса пытается вызвать конструктор интерфейса, и он не определен, а объявлен. Либо предоставьте определение, либо удалите объявление
  • Деструктор: другие соображения, оставленные в покое (например, аналогичные рассмотренные выше), ваш деструктор виртуальный Каждая не чистая виртуальная функция должна иметь определение (поскольку по определению используется ).

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

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

3 голосов
/ 23 декабря 2011

Вы забыли добавить пустую функцию для виртуального деструктора. Тело функции на самом деле ничего не делает, и C ++ может поместить код низкоуровневого уничтожения в деструктор производного класса (не совсем уверен в этом), но это все еще требуется:

#ifndef _I_LOG_H
#define _I_LOG_H

struct ILog {
    virtual ~ILog();
    // virtual ~ILog() = 0; // either works

    virtual void LogInfo(const char* msg, ...) = 0;
    virtual void LogDebug(const char* msg, ...) = 0;
    virtual void LogWarn(const char* msg, ...) = 0;
    virtual void LogError(const char* msg, ...) = 0;
};

#endif

Файл CPP:

ILog::~ILog()
{ // this does get called
}

Обновленный пример:

#include <iostream>

struct Monkey
{
    int data;
};

struct ILog
{
    ILog() : monkey(0) {}
    virtual ~ILog() = 0;

    virtual void LogInfo(const char* msg, ...) = 0;
    virtual void LogDebug(const char* msg, ...) = 0;
    virtual void LogWarn(const char* msg, ...) = 0;
    virtual void LogError(const char* msg, ...) = 0;

    void storeMonkey(Monkey* pM)
    {
        delete monkey;
        monkey = pM;
    }

    void message()
    {
        std::cout << "monkey->data contains " << monkey->data;
    }

private:
    Monkey* monkey;
};

struct ILogD : ILog
{
    int data;

    ILogD(Monkey* pM)
    {
        storeMonkey(pM);
    }

    void LogInfo(const char* msg, ...) {};
    void LogDebug(const char* msg, ...) {};
    void LogWarn(const char* msg, ...) {};
    void LogError(const char* msg, ...) {};
};

ILog::~ILog()
{
    delete monkey;
}



int main()
{
    ILogD o(new Monkey());

    o.message();
}
0 голосов
/ 23 декабря 2011

Просто предоставьте встроенную версию конструктора и деструктора, и компилятор не сгенерирует ссылку на них, чтобы компоновщик не включился.

ILog() {};
virtual ~ILog() {};
...