Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;-虚拟析构函数和链接器错误_C++_Inheritance_Linker_Abstract Class - Fatal编程技术网

C++ C++;-虚拟析构函数和链接器错误

C++ C++;-虚拟析构函数和链接器错误,c++,inheritance,linker,abstract-class,C++,Inheritance,Linker,Abstract Class,我有一个我写过的界面: #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, .

我有一个我写过的界面:

#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; }

您没有定义构造函数和析构函数,只声明了它们

试一试

  • 构造函数:一旦你声明了一个构造函数,任何构造函数,编译器都不会为你生成默认的构造函数。派生类的构造函数尝试调用接口的构造函数,但它没有定义,只是声明了。要么提供定义,要么删除声明
  • 析构函数:您的析构函数是虚拟的。每个非纯虚函数必须有一个定义(因为它是按定义使用的)
我还能执行虚拟析构函数定义中的东西吗, 或者它会简单地调用我的派生类析构函数并跳过它? 比如,这会触发吗


是的,你可以。当调用派生类的析构函数时,它将自动调用基类的析构函数。然而,我想不出在接口的析构函数中做什么有意义。但从技术上讲,您可以在析构函数中执行任何操作,即使它是虚拟的

只要提供构造函数和析构函数的内联版本,编译器不会为链接器生成引用

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

您忘记为虚拟析构函数添加空函数。函数体实际上不做任何事情,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();
}
#包括
结构猴
{
int数据;
};
结构ILog
{
ILog():monkey(0){}
virtual~ILog()=0;
虚拟void LogInfo(const char*msg,…)=0;
虚拟void LogDebug(const char*msg,…)=0;
虚拟void LogWarn(const char*msg,…)=0;
虚空日志错误(const char*msg,…)=0;
猴子(猴子*pM)
{
删除猴子;
猴子=pM;
}
无效消息()
{

std::coutAll不会丢失!除了模板类之外,会调用纯虚拟析构函数。 C++没有真正的接口,但是纯抽象类的工作方式与所有虚拟函数的设置一样,即创建空函数表0。大多数C++程序员避开了除了纯抽象类之外的任何事物的多重继承,因为不同编译器的实现方式存在复杂性和差异。 你的类不是一个纯虚拟类,因为你有成员数据,你需要一个不是纯虚拟函数的析构函数来清理它

您的结构类需要如下所示:

#ifndef _I_LOG_H 
#define _I_LOG_H
 
 struct ILog {
     virtual ~ILog() = 0;  // JDM: This is how you make it abstract
     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
现在,正确的方法是在ILog.cpp中:

#include "Ilog.h"
// only for the dtor
ILog::~ILog(){
    // code here will get called!
}
我确实提到了一些关于模板的内容,这些内容超出了您的问题范围,但必须理解。必须为模板类实现专门的纯虚拟析构函数:

 #ifndef _I_LOG_H 
 #define _I_LOG_H
 
 template<class T> class ILog {
     virtual ~ILog() = 0;  // JDM: This is how you make it abstract
     virtual void LogInfo(T msg, ...) = 0;
     virtual void LogDebug(T msg, ...) = 0;
     virtual void LogWarn(T msg, ...) = 0;
     virtual void LogError(T msg, ...) = 0;
 };
 #endif
然后我将您的类与LogMsg一起使用:

#include "ILog.h"
class Log : ILog<LogMsg> {
   // implement ILog...
   virtual ~Log();
 }
#包括“ILog.h”
类日志:ILog{
//实现ILog。。。
virtual~Log();
}
在我的CPP中:

#include "Log.h"
Log::~Log() {
   // this gets called
}
// Link error without the following
template<class LogMsg> ILog<LogMsg>::~ILog {
   // This gets called.
}
#包括“Log.h”
日志::~Log(){
//这被称为
}
//链接错误,但不包含以下内容
模板ILog::~ILog{
//有人打电话来。
}

我可以在virtuals析构函数定义中执行某些操作吗?更新了我的question@KaiserJohaan:当然可以,但是如果接口只声明纯虚拟函数,您会怎么做?您会怎么做?是派生类(接口的实现者)这是否应该清理它们使用的资源(如果有的话)。@KaiserJohaan:更新了我的回答“您忘记为纯虚拟析构函数添加空函数了。”这里的析构函数不是纯虚拟的。语法也不应该是纯虚拟的,即使析构函数在技术上不是。我不明白你在说什么对不起。我想说的是,在最初的问题中,析构函数只是虚拟的,不是纯虚拟的。我的版本可以工作,他的版本不行。但是如果你想让我删除我的答案,我会我非常高兴。只需否决几次投票,这样我就可以拿到徽章。哈哈。你的解决方案很好,我没有说不行。我只是想说你写的OP的dtor是纯虚拟的,而事实并非如此。我只是想让你改进你的答案。我无意否决它。这是一个很好的答案。但我也不能否决它虽然它包含不正确的信息,但可能重复
#include "ILog.h"
class Log : ILog<LogMsg> {
   // implement ILog...
   virtual ~Log();
 }
#include "Log.h"
Log::~Log() {
   // this gets called
}
// Link error without the following
template<class LogMsg> ILog<LogMsg>::~ILog {
   // This gets called.
}