- 都是释放子类的堆区资源。
- 都需要具体实现。
- 使用纯虚析构函数的父类属于抽象类,不能实例化具体对象。代码示例如下:
#include
// 父类 class Base { public: // 纯虚析构函数 virtual ~Base() = 0; }; // 纯虚析构函数的实现,此处必须实现,因为是析构函数,可能会释放父类的堆区资源,否则代码会报错 Base::~Base() { std::cout << "父类的纯虚析构函数调用" << std::endl; } class Son : public Base { public: // 子类析构函数的实现 virtual ~Son() { std::cout << "子类的析构函数调用" << std::endl; } }; int main() { // 实例化失败,此处代码会报错,因为父类有纯虚析构函数,是抽象类 // Base * base = new Base; // 实例化成功,因为子类不是抽象类 Base * base = new Son; system("pause"); return 0; } - 使用虚析构函数的父类不属于抽象类,可以实例化具体对象。代码实例如下:
#include
class Father { public: // 父类的虚析构函数 virtual ~Father() { std::cout << "父类的虚析构函数调用" << std::endl; } }; int main() { // 此时父类实例化成功,因为父类不是抽象类,代码不会报错 Father * father = new Father; system("pause"); return 0; }
解决父类指针或父类引用无法释放子类资源导致的内存泄露问题 。
现在可以来做个对比,首先写一个无虚析构或无纯虚析构的父类代码案例,看看最后的打印信息
- 无虚析构或无纯虚析构的父类代码案例:
#includeusing namespace std; class Father { public: // 父类的析构函数 ~Father() { std::cout << "父类的析构函数调用" << std::endl; } }; class Son : public Father { public: // 子类的析构函数 ~Son() { std::cout << "子类的析构函数调用" << std::endl; } }; int main() { Father * father = new Son; delete father; system("pause"); return 0; }
运行结果截图如下,可见未调用子类的析构函数,如果此时的子类申请了堆区资源,必将造成内存泄露问题:
- 有虚析构或纯虚析构的父类代码案例
#includeclass Father { public: // 父类的构造函数 Father() { std::cout << "父类的构造函数调用" << std::endl; } // 父类的虚析构函数 virtual ~Father() { std::cout << "父类的虚析构函数调用" << std::endl; } }; class Son : public Father { public: // 子类的构造函数 Son(std::string name) { std::cout << "子类构造函数的调用" << std::endl; // 子类申请堆区资源 m_Name = new std::string(name); } ~Son() { std::cout << "子类析构函数的调用" << std::endl; // 析构子类的堆区资源 if (m_Name != NULL) { delete m_Name; m_Name = NULL; } } // 此处为字符串指针变量,是为了申请堆区资源做准备 std::string * m_Name; }; int main() { Father * father = new Son("Jack"); delete father; system("pause"); return 0; }
运行结果截图如下,在此段代码中,父类使用了虚析构函数,子类申请了堆区资源,在释放父类资源的同时也释放了子类资源。
如果喜欢此文章,可以点赞哦,我会无偿分享所有源码与自己的C++成长之路 !



