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.
}