精华内容
下载资源
问答
  • 浅析:类只能有一个析构函数

    千次阅读 2015-04-01 12:42:11
    的构造函数与析构函数都是与同名(析构函数需要加上'~'),没有返回值的;...但是由于析构函数是没有参数的,那么从而无法通过参数的不同来达到重载多个析构函数的目的,从而析构函数只能有一个析构函数是由系

    类的构造函数与析构函数都是与类同名(析构函数需要加上'~'),没有返回值的;而构造函数是可以有参数的,但是析构函数是不能有参数的。

    我们知道类可以有多个构造函数,这些构造函数是重载函数的关系,也就是说函数名都是相同的,区别它们主要靠参数的不同(参数个数和参数类型)。

    但是由于析构函数是没有参数的,那么从而无法通过参数的不同来达到重载多个析构函数的目的,从而析构函数只能有一个。

    析构函数是由系统负责调用的,也可以手工显示调用,但是显示调用析构函数是不会析构该对象的,也就是说显示调用析构函数和没有显示调用析构函数是一样的,

    #include<iostream>
    using namespace std;
    class A
    {
    public:
       A(){};
       void fun() {cout<<"The object exists!";};
       ~A(){};

      int bbb;
    };
    int main()
    {
       A *a=new A();

      a->bbb=345;
       a->~A();//显示调用析构函数后,对象a还存在吗?
       a->fun();
       //编译运行后a->fun()能够输出,对象a仍存在
       return 0;

    再来看看汇编代码

    在VC的DEBUG模式下,通过查看反汇编代码,可以看到,直接调用析构函数的语句没有任何汇编代码对应它,就和没有这一语句一模一样.由此看来,析构函数是自动调用的,如果显示调用是不会析构对象的。
    ……
    14:       a->bbb = 345;
    0040105B   mov         ecx,dword ptr [ebp-4]
    0040105E   mov         dword ptr [ecx],159h
    15:       a->~A();
    16:       a->fun();
    00401064   mov         ecx,dword ptr [ebp-4]
    00401067   call        @ILT+15(A::fun) (00401014)
    ……

    由以上可知,一个类只能有一个析构函数,析构函数没有返回值没有参数,只能由系统调用时才析构对象。

    展开全文
  • 这篇文章用于总结当析构函数是普通析构函数、虚析构函数、纯虚析构函数时,我们使用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

    展开全文
  • C++中析构函数为虚函数

    千次阅读 多人点赞 2019-04-12 23:13:17
    就会调用该指针指向的派生类析构函数,而派生析构函数又自动调用基类的析构函数,这样整个派生的对象完全被释放。 (2)析构函数不定义为虚函数时:编译器实施静态绑定,在删除基类指针时,只会调用基类的...

    1、析构函数是否定义为虚函数的区别

    (1)析构函数定义为虚函数时:基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。

    (2)析构函数不定义为虚函数时:编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。

    下面看几个例子

    2、派生类指针操作派生类对象,基类析构函数不是虚函数

    #include<iostream>
    using namespace std;
    class ClxBase{
    public:
    	ClxBase() {};
    	~ClxBase() { cout << "Output from the destructor of class ClxBase!" << endl; };
    
    	void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
    };
    
    class ClxDerived : public ClxBase{
    public:
    	ClxDerived() {};
    	~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
    
    	void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
    };
    int main(){
        ClxDerived *p = new ClxDerived;		// 派生类指针操作派生类对象
    	p->DoSomething();
    	delete p;
    	system("pause");
    	return 0;
    }

    注:派生类指针操作派生类对象,基类析构函数不是虚函数,此时会先是否派生类的资源,再释放基类的资源,这里资源就不会出现泄漏的情况。

     

    3、基类指针操作派生类对象,基类析构函数不是虚函数

    #include<iostream>
    using namespace std;
    class ClxBase{
    public:
    	ClxBase() {};
    	~ClxBase() { cout << "Output from the destructor of class ClxBase!" << endl; };
    
    	void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
    };
    
    class ClxDerived : public ClxBase{
    public:
    	ClxDerived() {};
    	~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
    
    	void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
    };
    int main(){
    	ClxBase *p = new ClxDerived;		// 基类指针操作派生类对象
    	p->DoSomething();
    	delete p;
    	system("pause");
    	return 0;
    }

    注:基类指针操作派生类对象,基类析构函数不是虚函数:此时只是释放了基类的资源,而没有调用派生类的析构函数。调用dosomething()函数执行的也是基类定义的函数。这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,造成内存泄漏。

     

    4、基类指针操作派生类对象,基类析构函数是虚函数

    #include<iostream>
    using namespace std;
    class ClxBase{
    public:
    	ClxBase() {};
    // 定义为虚函数
    	virtual ~ClxBase() { cout << "Output from the destructor of class ClxBase!" << endl; };
    
    	virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
    };
    
    class ClxDerived : public ClxBase{
    public:
    	ClxDerived() {};
    	~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
    
    	void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
    };
    int main(){
    	ClxBase *p = new ClxDerived;		// 基类指针操作派生类对象
    	p->DoSomething();
    	delete p;
    	system("pause");
    	return 0;
    }

    注:基类指针操作派生类对象,基类析构函数是虚函数:此时释放了派生类的资源,再调用基类的析构函数。调用dosomething()函数执行的也是派生类定义的函数。

            在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员。如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数。

            析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。

     

    5、基类析构函数定义为虚函数的情况

            如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销.当类里面有定义虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,并且有使用到基类指针操作派生类的情况时,才把析构函数写成虚函数。

    展开全文
  • C++析构函数 ...注意:析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。如果用户没有定义,那么编译器会自动生成。 析构函数举例: #include using namespace std; class Student{ p
  • C++析构函数

    2019-09-23 08:45:21
    C++析构函数 一、认识析构函数 析构函数也是一种特殊... 一个类只能有有一个析构函数,如果没有显式的定义,系统会生成一个缺省的析构函数(合成析构函数)。 因为无参数无返回值析构函数不能重载。每一次构...

    C++析构函数

    一、认识析构函数

    析构函数也是一种特殊的成员函数。它执行与构造函数相反的操作,通常用于撤消对象时的一些清理任务,如释放分配给对象的内存空间等。

    同样的,我们来看看析构函数的几个特点:

    • 函数名是在类名前加上~,无参数且无返回值。
    • 一个类只能有且有一个析构函数,如果没有显式的定义,系统会生成一个缺省的析构函数(合成析构函数)。
    • 因为无参数无返回值析构函数不能重载。每有一次构造函数的调用就会有一次析构函数的调用。
    • 当撤消对象时,编译系统会自动地调用析构函数。 如果程序员没有定义析构函数,系统将自动生成和调用一个默认析构函数,默认析构函数只能释放对象的数据成员所占用的空间,但不包括堆内存空间。
    class Data{
        public:
            Data(int year=2019,int month=9,int day=23):_year(year),_month(month),_day(day){}
            ~Data(){
        cout<<"~Data()"<<this<<endl;
    }
        private:
            int _year=1990;
            int _month;
            int _day;
    };
    void test(){
        Data test1;
    }
    int main(){
        test();
        return 0;
    }

    在test()函数中构造了对象d1,那么在出test()作用域d1应该被销毁,此时将调用析构函数,下面是程序的输出。当然在构建对象时是先调用构造函数的,在这里就不加以说明了。

    析构函数被调用的两种情况

    1)若一个对象被定义在一个函数体内,当这个函数结束时,析构函数会被自动调用。

    2)若一个对象在使用过程中运用new运算符进行动态创建,在使用delete释放时,自动调用析构函数。

    二、销毁操作

    析构函数在作用完类对象离开作用域后释放对象使用的资源,并销毁成员。

    void test(){
        int a=1;
        int b=2;
    }

    在一个函数体内定义一个变量,在test函数中定义a和b两个变量,在出了test函数后,a和b就会被销毁(栈上的操作)。如果是一个指向动态开辟的一块空间的指针(new,malloc),我们都需要进行free,否则就会内存泄露问题。

    当类类型对象的成员还有一个类类型对象,那么在析构函数里也会调用这个对象的析构函数。

    缺省的析构函数

    每个类都必须有一个析构函数。

    如果类中没有显式地为一个类定义析构函数,编译系统会自动地生成一个缺省的析构函数

    类名::析构函数命(){}

    class Date{
        public:
            Date(char *){
                str=new char[max_len];
        }
        ~Date(){ delete []str;}
        void get_info(char *);
        void send_info(char *);
        private:
            char *str;
            int max_len;
    };

    析构函数阻止该类型对象被销毁

    我们如果不想要析构函数来对对象进行释放该怎么做呢,不显式的定义显然是不行的,因为编译器会生成默认的合成析构函数。之前我们知道了如果想让系统默认生成自己的构造函数可以利用default,那么其实还有一个东西叫做delete。

    class Date{
        public:
           Date(int year=2019,int month=9,int day=1):_year(year),_month(month),_day(day){}
            ~Date()=delete;
        private:
            int _year=2019;
            int _month;
            int _day;
    };

    这么写了,又在底下创建Date类型的对象,那么这个对象将是无法被销毁的,其实编译器并不允许这么做,直接会给我们报错。

    但可以使用动态创建这个类类型对象的,像这样:Date* p = new Date;虽然这样是可行的,但当你delete p的时候依然会出错。既不能定义一个对象也不能释放动态分配的对象,所以还是不要这么用为好。

    一般在显式的定义了析构函数的情况下,应该也把拷贝构造函数和赋值操作显式的定义。

    class Date{
        public:
           Date(int year=2019,int month=9,int day=1):_year(year),_month(month),_day(day){
                p=new int;
    }
            ~Date(){
                delete p;
    }
        private:
            int _year=2019;
            int _month;
            int _day;
            int *p;
    };

    成员中有动态开辟的指针成员,在析构函数中对它进行了delete,如果不显式的定义拷贝构造函数,当你这样:Date d2(d1)来创建d2,我们都知道默认的拷贝构造函数是浅拷贝,那么这么做的结果就会是d2的成员p和d1的p是指向同一块空间的,那么调用析构函数的时候回导致用一块空间被释放两次,程序会崩溃。

     

    调用构造函数与析构函数的顺序

    1)一般顺序

    调用析构函数的次序正好与调用构造函数的次序相反,最先被调用的构造函数,其对应的构造函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。

    对象1构造函数->对象2的构造函数->对象3的构造函数->对象3的析构函数->对象2的析构函数->对象1的析构函数

    2)全局对象

    在全局范围中定义的对象(即在所有函数之外定义的对象),它的构造函数在所有函数执行之前调用。在程序流程离开其作用域时,调用该全局对象的析构函数。(包括main函数)

    3)auto局部对象

    局部自动对象(例函数中定义的对象),则在建立对象时调用其构造函数。如果函数被多次调用,则每次调用时都要调用构造函数。在函数调用结束、对象释放时先调用析构函数。

    4)static局部对象

    在函数中定义静态局部对象,则只在程序第一次盗用此函数建立对象时调用构造函数一次,在调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用析构函数。

    对象的生存期

    对象生存期不同分为:局部对象、全局对象、静态对象、动态对象。

    (1)局部对象

    当对象被定义时,调用构造函数,该对象被创建;当程序退出该对象所在的函数体或程序块时,调用析构函数,对象被释放。

    局部对象在被定义在一个函数体或程序块内的,它的作用域限定在函数体或程序块内,生存期比较短。

    (2)全局对象

    当程序开始运行时,调用构造函数,该对象被创建;当程序结束时,调用析构函数,该对象被释放。

    静态对象时被定义在一个文件中,它的作用域从定义是起到文件结束为止,生存期长。

    (3)静态对象

    当程序中定义静态对象时,调用构造函数,该对象被创建;当整个程序结束时,调用析构函数,对象被释放。

    全局对象是被定义在某个文件中,它的作用域包含在该文件的整个程序中,生存期最长。

    (4)动态对象

    执行new运算符调用构造函数,动态对象被创建;用delete释放对象时,调用析构函数

    动态对象由程序员掌握,它作用域与生存期是有new和delete之间的时间决定的。

     

    展开全文
  • C#析构函数

    千次阅读 2017-01-11 19:04:36
    一个类只能有一个析构函数。 无法继承或重载析构函数。 无法调用析构函数。它们是被自动调用的。 析构函数既没有修饰符,也没有参数。 例如,下面是类 Car 的析构函数的声明: class Car {
  • 一个类只能有一个析构函数。 无法继承或重载析构函数。 无法调用析构函数。它们是被自动调用的。 析构函数既没有修饰符,也没有参数。 例如,下面是类 Car 的析构函数的声明: class Car { ~Car() // destructor ...
  • C++ 析构函数

    万次阅读 多人点赞 2016-09-21 00:02:15
    C++ 析构函数我使用的继承开发环境: Visual Studio 2010设计一个类时,如何写析构函数析构函数如果我们不写的话,C++ 会帮我们自动的合成一个,就是说:C++ 会自动的帮我们写一个析构函数。很多时候,自动生成...
  • 我知道,基类的析构函数要生命为虚函数,不然用基类指针删除派生对象时只会调用基类的析构函数,而不会调用派生析构函数,从而发生内存泄露。但是为什么会这样?为什么基类析构函数不是虚函数时,就不会调用...
  • 析构函数

    千次阅读 2018-08-01 19:45:58
    1)构造函数可以多个来构成重载,但析构函数只能有一个,不能构成重载 2)构造函数可以参数,但析构函数不能参数 3)与构造函数相同的是,如果我们没有显式的写出析构函数,那么编译器也会自动的给我们加上...
  • 析构函数的作用及合成析构函数

    千次阅读 2013-11-30 02:05:33
    析构函数 用于释放在构造函数或在对象生命期内获取的资源。 如果需要析构函数,那么也需要复制操作符和复制构造函数。...编译器总是为我们合成一个析构函数,合成析构函数按对象创建时的逆序撤销每个非static成员,
  • C++析构函数调用和虚析构函数

    千次阅读 2013-11-13 08:48:40
    析构函数一般是自动调用的,但是某些情况下页需要显式调用,如出栈和入栈操作: void C::push_back(const X& a) { // ... new(p) X{a}; // copy constr uct an X with the value a in address p // ... }   void C:...
  • 类型需要虚析构函数的另外一个特征 是该类型具有指针成员或引用成员。如果指针成员和引用成员,则该类型通常需要实现析构函数以及拷贝操作。 通常,一个实现了析构函数的类型同时也需要实现拷贝构造函数与拷贝...
  • 析构函数:当且仅当里包含至少一个虚函数的时候才去声明虚析构函数,无故的声明虚析构函数和永远不去声明一样是错误的; class <类名> { virtual ~<类名>(); … }; 即:virtual ~A(); 纯虚析构函数:抽象是...
  • 默认构造函数指不带参数或者所有参数都缺省值的构造函数!...每个只有一个析构函数一个赋值函数,但可以多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不编写上述
  • 1. 析构函数和虚析构函数 如果基类的析构函数是虚的,那么它的派生析构函数都是虚的 这将导致:当派生析构的时候,它的所有的基类的析构函数都将得到调用 否则,只调用派生析构函数(这可能导致基类的...
  • 如果你有一个虚函数功能的,则它需要一个析构函数,原因如下 : 1.  如果一个类有虚函数功能,它经常作为一个基类使用。 2. 如果它是一个基类,它的派生经常使用new来分配。 3. ...
  • C++析构函数

    2014-05-21 09:23:02
    在定义的时候就要定义析构函数,并且析构函数没有返回值也没有参数,因此不可以重载,开发者可以在最后使用次对象之后做任何操作,例如输出一些价值的信息。如果在定义时未定义析构函数,则在对象使用结束后...
  • C#析构函数

    万次阅读 2016-09-08 14:15:58
    转载自http://www.cnblogs.com/paper/archive/2009/07/31/1535998.html析构函数...析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。以C++语
  • 析构函数(不能重载,没有参数,一个类只能有一个析构函数。如果没定义,编译器会自动生成一个析构函数:创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理...
  • 将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。 C++默认的***析构函数不是虚函数是因为虚函数需要...
  • 私有析构函数

    2015-01-09 21:56:44
    析构函数定义为私有成员可以保证只能在堆上new一个新的对象. 下面写一个简单的例子验证一下: 写一个简单的,把它的析构函数变成私有的. new一个新的对象,编译通过,如下图. 把原来的代码改动一下,直接在...
  • C++-析构函数

    2017-02-20 10:28:42
    析构函数没有返回类型,没有参数表,不能重载,一个类只能有一个析构函数析构函数只能由系统自动调用,不能在程序显示调用。 当创建一个对象时,C++将首先为数据成员分配存储空间,接着调用构造函数对成员进行...
  • 浅谈析构函数

    2015-04-22 11:18:31
    一个类只能有一个析构函数析构函数没有返回类型,也没有参数。析构函数的名称是在名称前面加上~。 如果构造函数使用new来分配内存,则必须提供使用delete的析构函数来释放这些内存。 下述情况下,析构...
  • C++类中被删除的析构函数

    千次阅读 2017-01-05 22:50:06
    在c++ primer(Page 553)中提到 如果在基类中有一个不可访问或删除掉的析构函数,则派生中合成的默认和拷贝构造函数将是被删除的 到底什么情况才会有删掉的析构函数呢?class Test{ public: Test(){ a=10; } int...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 66,046
精华内容 26,418
关键字:

一个类中只能有一个析构函数