析构函数 订阅
析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。 展开全文
析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。
信息
外文名
destructor
命    名
与类名相同,在前面加位取反符~
应用学科
计算机科学
相    反
构造函数
中文名
析构函数
目    的
清理善后” 的工作
析构函数函数介绍
与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。以C++语言为例:析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。 [1] 
收起全文
精华内容
下载资源
问答
  • 析构函数

    2019-08-16 20:08:15
    1、析构函数是特殊的成员函数 ~类名(void) { } 无参数、无返回值、不能重载 2、谁来调用? 析构函数会在对象被销毁时自动调用,在整个生命周期内最多被调用一次。 3、析构函数负责什么 析构函数会释放构造...

    1、析构函数是特殊的成员函数

    ~类名(void)

    {
    }

    无参数、无返回值、不能重载

    2、谁来调用?

    析构函数会在对象被销毁时自动调用,在整个生命周期内最多被调用一次。

    3、析构函数负责什么

    析构函数会释放构造函数所获取的所有资源,它的执行过程如下:

    A、先执行析构函数本身的代码

    B、调用成员类的析构函数

    C、调用父类的析构函数

    缺省的析构函数:

    如果一个类没有实现析构函数,编译器会自动生成一个具有析构函数功能的二进制指令,它负责释放编译器所能看到的资源(成员变量、类成员、父类成员),这就是缺省析构。

    如果类中没有动态资源,也不需要做清理的工作,那么缺省析构完全够用了,不需要实现新的析构函数。

    注意:缺省析构无法释放动态资源

     

     

     

     

     

     

     

     

    展开全文
  • 这篇文章用于总结当析构函数是普通析构函数、虚析构函数、纯虚析构函数时,我们使用delete运算符删除一个指针对象时,析构函数会有什么情况发生;普通析构函数CBase是基类,CDerive是其子类,类源码代码如下:class ...

    我们知道对象在结束其生命周期之前,都会调用析构函数以完成必要的清理工作;派生类调用的析构函数顺序是“先子类,后基类”; 这篇文章用于总结当析构函数是普通析构函数、虚析构函数、纯虚析构函数时,我们使用delete运算符删除一个指针对象时,析构函数会有什么情况发生

    ####普通析构函数####

    CBase是基类,CDerive是其子类,类源码代码如下:

    class CBase
    {
    public:
        CBase(){}
        //基类析构函数
        ~CBase(){ cout << "CBase Destructor" << endl; } 
    private:
        int a;
    };
    
    class CDerive:public CBase
    {
    public:
        CDerive(){}   
        //子类析构函数
        ~CDerive(){ cout << "CDerive Destructor" << endl; }
    private:
        int b;
    };
    

    测试代码如下:

    //case1
    CDerive *pDeriveObj = new CDerive();
    delete pDeriveObj;
    
    //case2
    //基类指针对象可以指向派生类,体现基类和派生类赋值兼容关系(不同类型可以转化和赋值),
    //但是pBaseObj只能访问基类成员,不能访问派生类成员
    CBase* pBaseObj = new CDerive();
    delete pBaseObj; //等价于删除基类对象
    

    测试结果:

    /* case 1
    先析构子类:CDerive Destructor
    后析构基类:CBase Destructor
    */
    
    /* case2
    仅析构基类:CBase Destructor
    */
    

    总结:

    1. 若delete运算符删除的是子类指针对象,则会调用子类和基类的析构函数;
    2. 若析构函数是非虚的,即使基类指针指向的是子类对象,则delete 指针对象时,也仅调用基类的析构函数

    ####虚析构函数####

    当基类中的析构函数设置为虚函数时,我们在delete 基类指针对象时,能根据实际类型完成对象的清理工作;源码如下:

    class CBase
    {
    public:
        CBase(){}
        //基类虚析构函数
        virtual ~CBase(){ cout << "CBase Destructor" << endl; } 
    private:
        int a;
    };
    
    class CDerive:public CBase
    {
    public:
        CDerive(){}   
        //子类虚析构函数
        virtual ~CDerive(){ cout << "CDerive Destructor" << endl; }
    private:
        int b;
    };
    

    测试代码:

     //指向基类对象
     CBase*pBaseObj_case1 = new CBase();
     delete pBaseObj_case1;
     
     //指向子类对象
     CBase*pBaseObj_case2 = new CDerive();
     delete pBaseObj_case2;
    

    运行结果:
    //case1
    CBase Destructor

    //case2
    CDerive Destructor ->先子类
    CBase Destructor ->后基类

    总结:
    当基类的析构函数为虚函数时,基类指针指向的是子类对象时,使用delete运算符删除指针对象,析构函能够按照“先子类,后基类”的原则完成对象清理;这样在多重继承的类中,能够保证每个类都能够得到正确的清理;比如基类和子类的缓冲区都能被释放;

    ####纯虚析构函数####

    当基类中有纯虚函数时,基类是不能被实例化的,需要在子类中重写该纯虚函数;对于纯虚析构函数有点特殊,源码如下:

    class CBase
    {
    public:
        CBase(){}
        //基类析构函数
        virtual ~CBase()= 0;
    private:
        int a;
    };
    
    class CDerive:public CBase
    {
    public:
        CDerive(){}   
        //子类析构函数
        virtual ~CDerive(){ cout << "CDerive Destructor" << endl; }
    private:
        int b;
    };
    

    测试代码:

    CBase*pBaseObj = new CDerive();
    delete pBaseObj;
    

    当我们编译代码时,会发现代码编译不过提示:
    “error LNK2019: 无法解析的外部符号 “public: virtual __thiscall CBase::~CBase(void)” (??1CBase@@UAE@XZ),该符号在函数 “public: virtual __thiscall CDerive::~CDerive(void)” (??1CDerive@@UAE@XZ) 中被引用”

    原因是子类析构时需要调用基类的析构函数,但发现代码中没有实现CBase析构函数,导致编译异常;
    解决办法就是我们在CBase类外实现其函数体,而不是在子类中重写,而且~CDerive函数也是虚函数,即使其函数名不同;这就是和其他纯虚函数有特殊的地方;

    我们需要在CBase类外增加如下的析构函数:

    CBase::~CBase()
    { 
        cout << "CBase Destructor" << endl; 
    } 
    

    这样得到的运行结果是:
    CDerive Destructor ->先子类
    CBase Destructor ->后基类

    ####总结####

    最好把基类的析构函数声明为虚函数。这将使所有派生类的析构函数自动成为虚函数。这样,如果程序中显式地用了delete运算符准备删除一个对象,而delete运算符的操作对象用了指向派生类对象的基类指针,则系统会调用相应类的析构函数。

    专业人员一般都习惯声明虚析构函数,即使基类并不需要析构函数,也显式地定义一个函数体为空的虚析构函数,以保证在撤销动态分配空间时能得到正确的处理。

    参考资料:

    http://c.biancheng.net/cpp/biancheng/view/247.html

    http://blog.csdn.net/yapian8/article/details/46418687

    展开全文
  • 析构函数

    析构函数的作用:

    在对象撤销前做必要的“清理现场”的工作,当派生类的对象从内存中撤销时,一般先调用派生类的析构函数,再调用基类的析构函数,但是如果,用new运算符动态生成一个派生类的堆对象,并让基类指针指向该派生类对象,当程序用delete运算符通过基类指针撤销派生类对象时,系统只会执行基类的析构函数,而不执行派生类的析构函数

    基类中有非虚构函数时的执行情况:

    #include<iostream>
    using namespace std;
    class point
    {
    public:
          point(){cout<<"point constructor"<<endl;}
          ~point(){ cout<<"executing point destructor"<<endl;}
    };
    class circle:public point
    {
    public:
    	circle(double r):radius(r){cout<<"circle constructor"<<endl;}
    	~circle(){ cout<<"executing circle destructor"<<endl;}
    private:
    	double radius;
    };
    int main()
    {
    	point *p=new circle(2.0);
    	delete p;
    	system("pause");
    }

    执行结果:


    上面只执行了基类point的析构函数,而没有执行派生类circle的析构函数,如果希望能执行派生类的析构函数,则应该将基类的析构函数声明为虚析构函数

    如:

    #include<iostream>
    using namespace std;
    class point
    {
    public:
    	point(){cout<<"point constructor"<<endl;}
        virtual~point(){ cout<<"executing point destructor"<<endl;}
    };
    class circle:public point
    {
    public:
    	circle(double r):radius(r){cout<<"circle constructor"<<endl;}
    	~circle(){ cout<<"executing circle destructor"<<endl;}
    private:
    	double radius;
    };
    int main()
    {
    	point *p=new circle(2.0);
    	delete p;
    	system("pause");
    }

    执行结果:


    先调用了派生类的析构函数,再调用了基类的析构函数,当基类的析构函数为虚函数时,无论指针指向的是同一类族中哪个对象,系统都会采用动态关联,调用相应的析构函数,对该对象进行清理工作

    如果将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不同,最好将基类的析构函数声明为虚函数,这将使所有派生类的析构函数自动成为虚函数,

    即使基类不需要析构函数,也显示地定义一个函数体为空的虚析构函数,以保证在撤销动态分配空间时能得到正确的处理。

    栈中对象-----构造函数与析构函数的调用

    #include<iostream>
    using namespace std;
    class point
    {
    public:
    	point(){cout<<"point constructor"<<endl;}
    	virtual ~point(){ cout<<"executing point destructor"<<endl;}
    };
    class circle:public point
    {
    public:
    	circle(double r):radius(r){cout<<"circle constructor"<<endl;}
    	~circle(){ cout<<"executing circle destructor"<<endl;}
    private:
    	double radius;
    };
    int main()
    {
    	{
        cout<<"point 栈对象,构造函数与析构函数"<<endl;
    	point p ;
    	}
    	{
        cout<<"circle 栈对象,构造函数与析构函数"<<endl;
    	circle c(2.0);
    	}
    	system("pause");
    }
    执行结果:

    当构造子类对象时,先调用父类构造函数,再调用子类构造函数,析构的时候,先调用子类析构函数,再调用父类的析构函数
    堆对象---构造函数和析构函数的调用情况
    (new 调用构造函数,delete调用析构函数,如果只new不delete,即使出了对象作用域,对象也不会析构,因为new分配的内存在堆空间)
    #include<iostream>
    using namespace std;
    class point
    {
    public:
    	point(){cout<<"point constructor"<<endl;}
    	virtual ~point(){ cout<<"executing point destructor"<<endl;}
    };
    class circle:public point
    {
    public:
    	circle(double r):radius(r){cout<<"circle constructor"<<endl;}
    	~circle(){ cout<<"executing circle destructor"<<endl;}
    private:
    	double radius;
    };
    int main()
    {
    	{
        cout<<"point 堆对象"<<endl;
    	point *p =new point();
    	delete p;
    	}
    	{
        cout<<"circle 堆对象"<<endl;
    	circle *c =new circle(2.0);
    	delete c;
    	}
    	{
        cout<<"circle 堆对象"<<endl;
    	point *p =new circle(2.0);
    	delete p;
    	}
    
    	system("pause");
    }
    执行结果:




    展开全文
  • 将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。 C++默认的***析构函数不是虚函数是因为虚函数需要...

    将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。

    C++默认的***析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存***。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。

    C++中析构函数的作用
    析构函数与构造函数对应,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。
    析构函数名也应与类名相同,只是在函数名前面加一个位取反符,例如stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。

    如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

    如果一个类中有指针,且在使用的过程中动态的申请了内存,那么最好显示构造析构函数在销毁类之前,释放掉申请的内存空间,避免内存泄漏。

    类析构顺序:1)派生类本身的析构函数;2)对象成员析构函数;3)基类析构函数。

    静态函数和虚函数的区别:
    静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制,调用的时候会增加一次内存开销

    请你说一说你理解的虚函数和多态
    多态的实现主要分为静态多态和动态多态,***静态多态主要是重载,在编译的时候就已经确定;动态多态是用虚函数机制实现的,在运行期间动态绑定。***举个例子:一个父类类型的指针指向一个子类对象时候,使用父类的指针去调用子类中重写了的父类中的虚函数的时候,会调用子类重写过后的函数,在父类中声明为加了virtual关键字的函数,在子类中重写时候不需要加virtual也是虚函数。
    虚函数的实现:在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。

    展开全文
  • 1、为什么要使用虚析构函数 我们知道析构函数是在对象生命周期结束时自动被调用,用来做一些清理工作(如释放句柄,释放堆内存等),防止出现内存泄漏。 那怎么还有虚析构函数呢? 使用虚析构函数的类一般是要...
  • 析构函数和protected析构函数 原文:https://blog.csdn.net/koudaidai/article/details/7717817 一、虚析构函数 类型需要虚析构函数的另外一个特征 是该类型具有指针成员或引用成员。如果有指针成员和引用...
  • 大家知道,析构函数是为了在对象不被使用之后释放它的资源,虚函数是为了实现多态。那么把析构函数声明为vitual有什么作用呢?直接的讲,C++中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说,如果派生类中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,789
精华内容 11,115
关键字:

析构函数