2010-08-16 2 views
1

У меня есть класс, предназначенный для импорта/экспорта данных в одном из нескольких разных форматов. Каждый формат должен иметь точно такой же интерфейс, так что я его реализация в качестве базового класса с кучей виртуальных методов и производным классом для каждого конкретного формата:Виртуальная функция C++ не найдена

#ifndef _IMPORTEXPORT_H_ 
#define _IMPORTEXPORT_H_ 

#include "stdio.h" 

enum EXPORT_TYPE { 
    EXPORT_INI = 1, 
}; 

class exportfile { 
public: 
    virtual ~exportfile(); 

    static exportfile * openExportFile(const char * file, EXPORT_TYPE type); 

    virtual void startSection(int id) = 0; 
    virtual void endSection() = 0; 

protected: 
    exportfile(const char * file); 
    FILE * hFile; 

}; 

class iniexportfile : public exportfile { 
public: 
    iniexportfile(const char * file) : exportfile(file) { } 

    void startSection(int id); 
    void endSection(); 
private: 
    bool inSection; 

}; 

#endif 

Это базовый класс (exportfile) и один из производных классов (iniexportfile).

Реализация этих методов являются такие:

#include "importexport.h" 

#include <exception> 
#include <assert.h> 

exportfile * openExportFile(const char * file, EXPORT_TYPE type) { 
    switch(type) { 
     case EXPORT_INI: 
      return new iniexportfile(file); 
     default: 
      return NULL; 
    } 
} 

exportfile::exportfile(const char * file) { 

     this->hFile = fopen(file, "w"); 

     if(this->hFile == 0) { 
      throw new std::exception("Unable to open export file"); 
     } 
} 

exportfile::~exportfile() { 
    assert(this->hFile != 0); 

    this->endSection(); 

    fclose(this->hFile); 
    this->hFile = 0; 
} 

void iniexportfile::startSection(int id) { 
    assert(this->hFile != 0); 

    fprintf(this->hFile, "[%d]\r\n", id); 

    this->inSection = true; 
} 

void iniexportfile::endSection() { 
    this->inSection = false; 
} 

(. Обратите внимание, класс, очевидно, не полный)

Наконец, у меня есть метод испытания:

#include "importexport.h" 

#include <exception> 

using namespace std; 

void runImportExportTest() { 
    iniexportfile file("test.ini"); 

    file.startSection(1); 

    file.endSection(); 
} 

Во всяком случае, все это прекрасно компилируется, но когда он привязывается, компоновщик вызывает эту ошибку:

error LNK2001: unresolved external symbol "public: virtual void __thiscall exportfile::endSection(void)" ([email protected]@@UAEXXZ) importexport.obj 

Почему он ищет exportfile::endSection(), когда он обозначен как чистый виртуальный? Я каким-то образом не делал это чисто виртуальным? Или, я был испорчен C#, и я полностью уничтожаю эти виртуальные функции?

Кстати, это Visual Studio 2008. Думаю, я должен упомянуть об этом где-то.

+0

'_IMPORTEXPORT_H_' является [зарезервированным идентификатором] (http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier/228797#228797). – GManNickG

+0

@GMan: Хотя это правда, это не имеет отношения к вопросу, верно? –

+0

Вот почему это комментарий. :) (Если только вас не беспокоило, что это приведет к разрыву кода, тогда да, это не так, как ваша проблема.) – GManNickG

ответ

2

К тому времени dtor вызывается:

exportfile::~exportfile() { 
    assert(this->hFile != 0); 

    this->endSection(); 

    fclose(this->hFile); 
    this->hFile = 0; 
} 

компилятор имеет «размотал» на виртуальные таблицы, так что будет решать функции exportfile::endSection() - это не будет вызывать производный версию. Вам нужно будет разработать другой метод очистки.

См. "Never call virtual functions during construction or destruction".

+0

* ударяет головой * Да, вы правы. Этот вызов должен идти в 'iniexportfile :: ~ iniexportfile'. Он отлично работает, когда я это делаю. –

+1

Конечно, если бы я строго сказал «нет виртуальных методов в моих деструкторах», как бы я это сделал. Извлеките его в не виртуальный метод и вызовите это из моего деструктора? –

+0

@Mike: есть моменты, когда было бы хорошо делать то, что вы хотели сделать там. Но я не уверен в лучшем шаблоне, чем в том, что он должен содержать требуемый код очистки в каждом из наиболее производных классов. –