C++学习24 虚析构函数

在C++中,构造函数用于在创建对象时进行初始化工作,不能声明为虚函数。因为在执行构造函数前对象尚未创建完成,虚函数表尚不存在,也没有指向虚函数表的指针,所以此时无法查询虚函数表,也就不知道要调用哪一个构造函数。下节会讲解虚函数表的概念。

析构函数则用于在销毁对象时完成相应的资源释放工作,可以被声明为虚函数。

为了说明虚析构函数的必要性,请大家先看下面一个例子:

#include <iostream>
using namespace std;
//基类
class Base{
private:
    int *a;
public:
    Base();
    ~Base(){ cout<<"Base destructor"<<endl; }
};
Base::Base(){
    a = new int[100];
    cout<<"Base constructor"<<endl;
}
//派生类
class Derived: public Base{
private:
    int *b;
public:
    Derived();
    ~Derived( ){ cout<<"Derived destructor"<<endl; }
};
Derived::Derived(){
    b = new int[100];
    cout<<"Derived constructor"<<endl;
}
int main( ){
   Base *p = new Derived;
   delete p;
   return 0;
}

本例中定义了两个类,基类 Base 和派生类 Derived,它们都有自己的构造函数和析构函数。在构造函数中,会分配100个 int 型的内存空间;在析构函数中,会把这些内存释放掉。

在 main 函数中,定义了基类类型的指针 p,并指向派生类对象,然后希望用 delete 释放 p 所指向的空间

从运行结果可以看出,执行delete p;语句时只调用了基类的析构函数,却没有调用派生类的析构函数。这会导致 b 所指向的 100 个 int 型内存空间得不到释放,除非程序运行结束被操作系统回收,否则就再也没有机会释放这些内存。这是典型的内存泄露。

内存泄露问题是程序员需要极力避免的。本例中出现的内存泄露是由于派生类的析构函数未被调用引起的,为了解决这个问题,需要将基类的析构函数声明为虚函数。修正后的代码如下所示:

class Base{
private:
    int *a;
public:
    Base();
    virtual ~Base(){ cout<<"Base destructor"<<endl; }
};
Base::Base(){
    a = new int[100];
    cout<<"Base constructor"<<endl;
}

如此,派生类的析构函数也会自动成为虚析构函数。当执行delete p;语句时,会先执行派生类的析构函数,再执行基类的析构函数,这样就不存在内存泄露问题了。

这个例子足以说明虚析构函数的必要性,但是如果不管三七二十一的将所有的基类的析构函数都声明为虚函数,也是不合适的。通常来说,如果基类中存在一个指向动态分配内存的成员变量,并且基类的析构函数中定义了释放该动态分配内存的代码,那么就应该将基类的析构函数声明为虚函数。

时间: 2024-10-13 23:01:14

C++学习24 虚析构函数的相关文章

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数、抽象类、虚析构函数、动态创建对象

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数.抽象类.虚析构函数.动态创建对象 一.纯虚函数 1.虚函数是实现多态性的前提 需要在基类中定义共同的接口 接口要定义为虚函数 2.如果基类的接口没办法实现怎么办? 如形状类Shape 解决方法 将这些接口定义为纯虚函数 3.在基类中不能给出有意义的虚函数定义,这时可以把它声明成纯虚函数,把它的定义留给派生类来做 4.定义纯虚函数: class <类名> { virtual <类型> <函

C++ Primer 学习笔记33_面向对象编程(4)--虚函数与多态(一):多态、派生类重定义、虚函数的访问、 . 和-&gt;的区别、虚析构函数、object slicing与虚函数

C++ Primer学习笔记33_面向对象编程(4)--虚函数与多态(一):多态.派生类重定义.虚函数的访问. . 和->的区别.虚析构函数.object slicing与虚函数 一.多态 多态可以简单地概括为"一个接口,多种方法",前面讲过的重载就是一种简单的多态,一个函数名(调用接口)对应着几个不同的函数原型(方法). 更通俗的说,多态行是指同一个操作作用于不同的对象就会产生不同的响应.或者说,多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为. 多态行分

C++学习(24)

1 //虚析构函数的学习 2 //如果外部程序使用new运算符定义了动态对象,则当外部程序结束时,要使用delete运算符删除该动态对象. 3 //但是如果外部程序利用赋值兼容原则,把动态申请的派生类对象地址赋给了基类对象指针,由于delete运算符隐含有析构函数的自动调用 4 //因此此时系统自动调用的必定是基类的析构函数,这就有可能引起内存泄漏问题 5 //下面是虚析构函数的例子 6 #include<iostream.h> 7 #include<string.h> 8 9 c

c++之虚析构函数

1.虚析构函数: 构造函数不能是虚函数.建立一个派生类对象时,必须从类     层次的根开始,沿着继承路径逐个调用基类的构造函数 析构函数可以是虚的.虚析构函数用于指引 delete 运算符正    确析构动态对象 2. 定义了基类虚析构函数,基类指针指向的 派生类动态对象也可以正确地用delete析构 3 设计类层次结构时,提供一个虚析构函数, 能够使派生类对象在不同状态下正确调用 析构函数 1 #include <iostream> 2 using namespace std; 3 4 c

构造函数、析构函数、虚析构函数、纯虚析构函数要点总结

1.派生类的构造函数.析构函数默认会调用基类的构造函数.析构函数. 顺序:基类构造.派生类构造:派生类析构,基类析构. 举例: 1 /****************************************************************************************************** 2 * File:Constructor&DestructorTest 3 * Introduction:测试构造函数.析构函数.虚构造函数与纯虚构造函数的一些

c++之——虚析构函数

先看代码: 1 #include<iostream> 2 using namespace std; 3 4 class Parent { 5 public: 6 Parent() :a(100), b(200), c(300) 7 { 8 9 p = new char[10]; 10 //strcpy(p, "abc"); 11 cout << "parent 无参构造...\n"; 12 } 13 Parent(int test) :a(1

虚析构函数的作用

1  class ITest 2  { 3  public: 4      virtual void Test() = 0; 5 6  }; 7 8  class CTest : public ITest 9  { 10 public: 11     CTest()  { printf("constructor.n"); }; 12     ~CTest() { printf("destructor.n"); }; 13     virtual void Test(

虚函数与虚析构函数原理

----------------siwuxie095 关于虚函数和虚析构函数的实现原理,因为涉及到 函数指针, 所以先介绍什么是函数指针 函数指针 如果通过一个指针指向对象,就称其为 对象指针,指针除了可以 指向对象之外,也可以指向函数,就称其为 函数指针 函数的本质,其实就是一段二进制的代码,它写在内存中, 可以通过指针来指向这段代码的开头,计算机就会从开头 一直往下执行,直到函数的结尾,并通过指令返回回来 如果有这么 5 个函数指针,它们所存储的就是 5 个函数的函数地址,当 使用时,如:使

c++对象内存模型之虚析构函数篇(2)

现在讨论第二种情况: (第一种情况传送门,单独一个类,有虚析构函数,虚函数) (2)有继承关系,单一继承,父类无虚析构函数,子类有(子类没有就没必要说了) 这种情况让我相当晕,照例先贴代码 : 1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 int ia; 7 public: 8 A ():ia(15) 9 { 10 } 11 ~A () 12 { 13 cout << "~A" &l