2

我在头文件中有所有类定义:ModelModule.h。我在下面提供了该文件的示例代码,其中我给出了 2 个类及其成员函数的声明:

#pragma once

#if !defined( MODELMODULE_H )
#define MODELMODULE_H


//Required header files

class CModelModule;
class COrdProbitMM;

class CModelModule
// virtual base class for all types of modeling modules
{
    friend class CSimCoordinator;
    friend class CHouseholdCoordinator;
    friend class CGenericHousehold;

    public:
        CModelModule(void);
        ~CModelModule(void);

    protected:
        std::string         m_Label;        
        std::vector<int>        m_AvailEndAttr;     
        void GetVarValues(std::vector<int>&, std::vector<double> &);


    public:


        virtual void    Configure(void){};
        virtual void loadXmlString(xmlNodePtr pXmlNode, xmlDocPtr pXmlDoc, xmlChar * con);
        virtual void    SaveXml(std::ofstream& fout){};

        double mrand(void); 
        double UniformRand ();          // returns a U[0,1] random number 
        double StdNormalRand ();        // returns a N(0,1) random number
};

class COrdProbitMM : public CModelModule
// Class represent the ordered-probit models
{
    friend class CSimCoordinator;
    friend class CHouseholdCoordinator;
    friend class CMMRunner;

    public:
        COrdProbitMM(CSimCoordinator& simcord, std::string& sLabel);
        COrdProbitMM(CSimCoordinator& simcord, std::string& sLabel, int nAlts);
        ~COrdProbitMM(void);

    private: 

        int             m_Max_nAlts;    
        std::vector<double>     m_Thresholds;   

    public:
        void    Configure(void);
        void    copyConfigure(COrdProbitMM* that);

        int  Run(CHouseholdObject*);
        int  Run(CPersonObject*);


        void loadXmlString(xmlNodePtr pConfNode,  xmlDocPtr pXmlDoc, xmlChar* con);

    private:
        int     Run(void);
};  

现在函数定义已在 .cpp 文件中给出:ModelModule.cpp。注:头文件已包含

#include "ModelModule.h"
//Other header files

//Code for all the other functions defined here

//Given below are the code for how the constructors and destructors are defined

COrdProbitMM::~COrdProbitMM(void)
{
}

CModelModule::CModelModule(void)
{
}

CModelModule::~CModelModule(void)
{   
}

我已经消除了任何语法错误的代码。但是,当我构建代码时,我得到了错误:* [ProjectName] Error1。在检查控制台时,我发现显示以下内容:

Building target: Project Name
Invoking: GCC C++ Linker
g++  -o "XYZ"  ./src/XYZ.o ./src/DataCache\ -\ Copy.o ./src/DataCache.o ./src/DataCoordinator.o ./src/DataObject.o ./src/HouseholdCoordinator.o ./src/
LinearEquation.o ./src/MMRunner.o ./src/MainFrm.o ./src/ModelModule.o ./src/SimCoordinator.o ./src/main.o   -lxml2 -lsqlite3

./src/ModelModule.o: In function `CModelModule::CModelModule()':
ModelModule.cpp:(.text._ZN12CModelModuleC2Ev[CModelModule::CModelModule()]+0xd): undefined reference to `vtable for CModelModule'
./src/ModelModule.o: In function `CModelModule::~CModelModule()':
ModelModule.cpp:(.text._ZN12CModelModuleD2Ev[CModelModule::~CModelModule()]+0xd): undefined reference to `vtable for CModelModule'

./src/ModelModule.o:(.rodata._ZTI12COrdProbitMM[typeinfo for COrdProbitMM]+0x8): undefined reference to `typeinfo for CModelModule'

collect2: ld returned 1 exit status
make: *** [Project Name] Error 1

**** Build Finished ****

我查看了这个论坛的 vtable 错误,并提到问题是当我们声明构造函数/析构函数但从未定义它时。但在这种情况下,这似乎不是问题,因为它在 ModelModule.cpp 中明确完成。这里似乎有一些非常基本的事情正在引起我的注意。

  • 我错过了什么?
  • 你能告诉我虚函数是什么以及它是如何导致错误发生的吗?
  • 它是否以某种方式与构造函数和析构函数联系起来?
4

1 回答 1

3

根本原因:
您收到错误是因为 C++ 标准要求除纯虚方法之外的类的所有虚方法都必须有定义[#1]

解决方案:
要么为所有方法提供定义,virtual要么使它们成为 pure virtual

解释:
gcc 在这种情况下产生的错误充其量是误导。这是一个示例程序,它演示了您遇到的问题:

class MyClass
{
    public:
    virtual void doSomething() { }
    virtual void doSomethingMore();
};

int main()
{
    MyClass obj;
    obj.doSomething();
    obj.doSomethingMore();
    return 0;
}

编译信息:

/home/4VqWl0/ccMjLi2V.o:在MyClass 的函数 vtable MyClass::doSomethingMore()' collect2:ld 返回 1 个退出状态 main':
prog.cpp:(.text+0x19): undefined reference to
.
prog.cpp:(.text+0x1e): undefined reference to

如您所见,GCC 在报告此类特定问题的错误方面是臭名昭著的。

它是否以某种方式与构造函数和析构函数联系起来?

gcc faq也记录了它:

ISO C++ 标准规定必须定义一个类的所有非纯虚方法,但不需要对违反此规则的任何诊断[class.virtual]/8。基于此假设,GCC 将仅在定义其第一个此类非内联方法的翻译单元中发出隐式定义的构造函数、赋值运算符、析构函数和类的虚拟表。

因此,如果您未能定义此特定方法,链接器可能会抱怨缺少明显不相关符号的定义。不幸的是,为了改善此错误消息,可能需要更改链接器,但并非总是如此。

解决方案是确保定义了所有不纯的虚拟方法。请注意,即使声明为 pure-virtual ,也必须定义析构函数[class.dtor]/7

好读:

“虚拟表”是未解决的外部是什么意思?


[#1] C++03 标准:10.3 虚函数 [class.virtual]

在类中声明的虚函数应在该类中定义或声明为纯(10.4),或两者兼而有之;但不需要诊断(3.2)。

于 2012-04-04T08:31:04.530 回答