C++ - 虚拟析构函数和链接器错误
Posted
技术标签:
【中文标题】C++ - 虚拟析构函数和链接器错误【英文标题】:C++ - virtual destructors and linker errors 【发布时间】:2011-12-22 21:23:03 【问题描述】:我得到了我写的这个界面:
#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;
【问题讨论】:
What is an undefined reference/unresolved external symbol error and how do I fix it?的可能重复 【参考方案1】:你还没有定义构造函数和析构函数,你只是声明了它们
试试
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;
;
构造函数:一旦您声明了构造函数,任何构造函数,编译器都不会为您生成默认构造函数。派生类的构造函数试图调用接口的构造函数,它没有被定义,只是被声明了。要么提供定义,要么删除声明
析构函数:其他考虑因素(例如,与上述类似的考虑因素)您的析构函数是虚拟的。每个非纯虚函数必须有一个定义(因为它根据定义使用)。
我还能在虚拟析构函数的定义中执行一些东西吗? 还是会简单地调用我的派生类析构函数并跳过它? 喜欢,这会触发吗
是的,你可以。当派生类的析构函数被调用时,会自动调用基类的析构函数。但是,我想不出在接口的析构函数中做些什么是有意义的。但从技术上讲,你可以在析构函数中做任何事情,即使它是虚拟的
【讨论】:
我可以在虚拟析构函数定义中做些什么吗?更新了我的问题 @KaiserJohaan:当然可以,但是如果 interface 只声明纯虚函数,你会怎么做?你会做哪些可能的清理工作?如果有的话,应该由派生类(接口的实现者)清理它们使用的资源。 @KaiserJohaan:更新了我的答案【参考方案2】:您忘记为虚拟析构函数添加一个空函数。函数体实际上并没有做任何事情,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();
【讨论】:
"您忘记为纯虚析构函数添加一个空函数。"这里的析构函数不是 pure 虚拟的。它不应该是 语法是纯虚拟的,尽管从技术上讲它不是析构函数。 我不明白你在说什么对不起。我要说的是,在最初的问题中,析构函数只是虚拟的,而不是 pure virtual 我的版本有效,他的无效。但是,如果您要我删除我的答案,我会非常高兴。只是投了几次票,所以我可以得到徽章。大声笑。 您的解决方案工作得很好,我没有说它没有。我只想说您写道,OP 的 dtor 是纯虚拟的,但事实并非如此。我只是想让你改进你的答案。我无意投反对票。这是一个很好的答案。但是,当它包含不正确的信息时,我也不能投票【参考方案3】:只需提供构造函数和析构函数的内联版本,编译器不会生成对它们的引用以使链接器失败。
ILog() ;
virtual ~ILog() ;
【讨论】:
【参考方案4】:一切都没有丢失!除了在模板类的情况下,纯虚析构函数被调用。 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
假设我有:
class LogMsg
const char* message;
LogMsg(const char * const msg)
message = msg;
// More Stuff
然后我将你的类与 LogMsg 一起使用:
#include "ILog.h"
class Log : ILog<LogMsg>
// implement 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.
【讨论】:
以上是关于C++ - 虚拟析构函数和链接器错误的主要内容,如果未能解决你的问题,请参考以下文章