纯虚析构函数应该在何处声明?

4

编辑:显然问题表述不够清晰。我遇到的问题是,当析构函数在头文件中定义时,它会添加到多个.obj文件中,从而导致链接器出现问题。实际问题是:

当我将析构函数添加到DLL项目中的CPP文件中,并使用动态加载和接口头文件使用dll时,基类析构函数是否仍然被调用以防止内存泄漏?

我正在使用MSVC 10.0,并具有实现接口的DLL项目。该接口是一个抽象(纯虚)基类。想法是使用头文件与库的动态加载。因此,我使用了纯虚析构函数来确保基类中的析构函数被调用。以下是示例代码以解释此内容:

//ISplitter.h
#pragma once

struct param {
    int something;
}

class ISplitter {
public:
    virtual ~ISplitter() = 0;
    virtual void useful() = 0;
}

ISplitter::~ISplitter() {
    /* Make sure base class destructor gets called */
}

主要实现头文件

//CSplitter.h
#pragma once
#include "CHelper.h"
#include "ISplitter.h"


class CSplitter : public ISplitter {
private:
    CHelper hlp;
public:
    ~CSplitter();
    void useful();
}

一些辅助类

//CHelper.h
#pragma once
#include "ISplitter.h" // I need the struct

// Class definition should go here but is irrelevant

现在问题是链接器生成了一个错误,告诉我析构函数:ISplitter::~ISplitter(void) 已经被多次声明,系统将无法构建。 错误:

CHelper.obj : error LNK2005: "public: virtual __cdecl ISplitter::~ISplitter(void)" (??1ISplitter@@UEAA@XZ) already defined in CSplitter.obj

怎么正确地解决这个问题?我已经将析构函数放在ISplitter.cpp中,但我担心如果我动态加载库并向上转换基类到ISplitter,这种方法可能行不通。


3
请发布真实的代码。答案是将析构函数定义移动到a.cpp文件中。 - user2100815
可能是C++中的纯虚析构函数的重复问题。 - Björn Pollex
@Neil:或者一开始就不将析构函数设为纯虚函数。为什么不只是一个空的虚析构函数呢? - Björn Pollex
@Wouter 我们不需要看到原始代码,但我们确实需要看到展示问题的真实C++代码。例如,在这里 ISplitter::ISplitter(),我假设缺少了一个~ - user2100815
@Neil:非常抱歉,缺少了那个部分,这是一个关键错误。请接受我的道歉并已经修复。 - Wouter Simons
如果您在头文件中定义一个函数、析构函数或者不是析构函数的函数,那么您必须将其声明为 inline。这样可以解决多重定义的问题。 - Bo Persson
2个回答

6
问题在于基类析构函数总是被调用,但在这种情况下,您已将其定义为纯虚拟函数,因此它不存在。只有在没有其他成员时,才需要将析构函数定义为纯虚拟函数来强制执行抽象类。在所有情况下都需要定义类的析构函数。
编辑:我错误地阅读了您的代码。只需将析构函数定义为虚拟内联即可。 virtual ~ISplitter() {} 在这里不需要任何纯虚拟函数,因为您已经有其他纯虚拟成员。

它确实存在,因此会出现多次定义错误,但这在他的“伪代码”中并不明显。 - user2100815
我相信这就是我所偏爱的,更加简洁明了,而且我确定它将会一直存在。因此,我接受这个答案。 - Wouter Simons
1
+1 是为了“只需在内联中定义析构函数”,但我还不能投票。他也可以通过将析构函数的带外定义标记为内联来进行定义。 - David Hammen
我认为inline关键字在优化设置不同的情况下会有不同的处理方式。 - Wouter Simons
@WouterSimons,“inline”关键字主要意味着“如果在多个编译单元中定义,则静默丢弃”(还有一些关于地址的额外内容),而在“class”或“struct”体中的成员函数隐式地是“inline”。根据C++标准,它与内联方法没有太大关系。 - Yakk - Adam Nevraumont

4

Sharptooth的回答是正确的,因为你必须提供纯虚析构函数的定义(请参考此GotW)。但是他的回答有误,因为你不能编写:

virtual ~A() = 0 {};

根据标准中的这个条款(尽管许多编译器支持此扩展),第10.4条第2段告诉我们什么是抽象类,并且作为附注,以下内容:
[注意:函数声明不能同时提供纯说明符和定义。—end note] [示例:]
struct C {
virtual void f() = 0 { }; // ill-formed
};

—示例结束]

有关详细信息,请参见我的这个问题


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接