C++ 纯虚析构函数 error LNK2019

本文探讨了C++编程中将基类的析构函数声明为纯虚函数的问题,这种做法虽然类似Java接口的设计,但在C++中会导致链接错误。文章详细解释了解决方案及构造函数和析构函数在继承中的特殊行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C#和Java写的多了,在能声明为接口的情况下,尽量不会声明为抽象类

昨天写C++程序,理所当然的把基类里的所有方法都写成了纯虚函数。事实上也的确应该全是纯虚函数。因为我是把两个(以后可能会是n个)硬件厂商的SDK抽象出来,硬件不一样,软件SDK差别就更大了,完全没有一致的地方。

基类的析构函数应该声明为虚函数的,索性一不做二不休声明为纯虚构函数好了。

这样,一个类似与Java中接口的C++抽象类就产生了,它只有头文件,源文件里只有两个#include,还是VS自动生成的。

等到我把实现写好,编译链接时报错:错误 1 error LNK2019: 无法解析的外部符号 "public: virtual __thiscall ::~CMyBaseClass(void)" ,该符号在函数 ******** 中被引用 

查了一下,析构函数必须要有函数体,哪怕你把他声明为纯虚函数。

析构函数,构造函数这些还是和普通的函数是不一样的。对照构造函数,派生类的构造函数必须先调用基类的构造函数。也许你没调用过,那是因为默认情况下调用无参数的默认构造函数。类似的,析构函数也是一样,他必然会调用基类的析构函数,不同的是,他是后调用基类析构函数的。所以基类的析构函数必须要有函数体。

给基类纯虚析构函数加了一个空的函数体,编译链接通过,在派生类析构函数开始除设置断点,单步跟踪,在执行完派生类析构函数后,跳到基类析构函数继续执行。

<think>好的,我现在需要回答用户关于C++虚析构函数的使用方法和示例的问题。首先,我得回忆一下虚函数和虚析构函数的基本概念。虚函数是在基类中声明但没有实现的虚函数,使得基类成为抽象类,无法实例化,派生类必须实现这些虚函数。但虚析构函数有点不同,因为即使它是虚的,仍然需要在基类中提供实现,否则链接时会报错。这是为什么呢? 根据用户提供的引用[3]和[4],当声明虚析构函数时,如果在类外没有定义,会导致链接错误,比如LNK2019。所以,虚析构函数需要同时在类内声明为虚,并在类外提供定义。比如,在基类中声明virtual ~Father() = 0;,然后在类外定义Father::~Father() { ... }。 接下来,我需要确认虚析构函数的使用场景。当希望一个类成为抽象基类,但没有其他虚函数时,可以将析构函数声明为虚的。这样,该类不能被实例化,但派生类必须实现自己的析构函数,同时基类的虚析构函数需要有自己的实现,以确保在销毁对象时能正确调用基类的析构函数。 然后,用户可能需要一个具体的示例来说明如何正确使用虚析构函数。比如,定义一个基类Animal,声明虚析构函数,并在类外定义它。派生类Dog继承Animal,并在析构函数中实现自己的逻辑。这样,当通过基类指针删除派生类对象时,会先调用派生类的析构函数,再调用基类的析构函数,确保资源正确释放。 需要注意的是,虚析构函数和其他虚函数的不同之处在于,即使它被声明为虚,仍然需要提供定义。这是因为当派生类对象被销毁时,基类的析构函数会被隐式调用,如果没有定义,链接器就会报错。这也是引用[4]中提到的必须再类外进行定义的原因。 另外,引用[1]和[2]提到了虚函数表和虚函数指针,虽然虚析构函数涉及虚函数机制,但具体实现细节可能不需要在回答中详细展开,除非用户的问题需要。不过可以提到,虚析构函数同样会影响虚函数表的结构,确保正确的析构顺序。 总结步骤:首先解释虚析构函数的作用,然后说明其声明和定义的语法,接着给出示例代码,并解释每个部分的作用,最后指出注意事项,如必须提供定义、派生类的处理等。同时,要引用提供的资料中的例子,如引用[3]和[4]中的错误情况,说明为什么必须定义虚析构函数。</think>### C++虚析构函数详解 #### 1. 核心概念 **虚析构函数**是一种特殊的虚析构函数,它通过`= 0`语法声明为虚函数,但需要显式定义实现。主要作用是将类标记为**抽象基类**,同时保证派生类对象销毁时能正确调用基类析构逻辑[^3][^4]。 #### 2. 语法规则 ```cpp class AbstractBase { public: virtual ~AbstractBase() = 0; // 声明虚析构函数 }; // 必须显式定义 AbstractBase::~AbstractBase() { // 基类析构逻辑 } ``` #### 3. 示例代码 ```cpp #include <iostream> using namespace std; class Animal { public: virtual ~Animal() = 0; // 虚析构声明 }; Animal::~Animal() { // 必须定义! cout << "Animal 析构" << endl; } class Dog : public Animal { public: ~Dog() override { // 派生类析构函数 cout << "Dog 析构" << endl; } }; int main() { Animal* animal = new Dog(); delete animal; // 输出顺序:Dog析构 -> Animal析构 return 0; } ``` #### 4. 关键特性 | 特性 | 说明 | |---------------------|----------------------------------------------------------------------| | 强制抽象类 | 包含虚析构函数的类无法实例化 | | 多态销毁保证 | 通过基类指针删除对象时,能正确调用派生类析构函数[^1] | | 必须定义实现 | 否则会导致链接错误(LNK2019)[^3] | | 调用顺序 | 派生类析构函数执行后,自动调用基类虚析构函数 | #### 5. 典型应用场景 - **接口定义**:创建抽象基类作为接口规范 - **资源管理**:基类需要统一释放共享资源(如文件句柄、内存池) - **多态销毁**:确保通过基类指针删除对象时的安全性[^2] #### 6. 注意事项 1. **定义位置**:虚析构函数必须在类外定义(即使函数体为空) 2. **继承链要求**:派生类无需显式实现基类虚析构函数,但仍需实现其他虚函数 3. **与普通虚函数区别**:虚析构函数不强制派生类覆盖,但需要显式定义
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值