精华内容
下载资源
问答
  • 一个类中只能有一个析构函数
    千次阅读
    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)
    ……

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

    更多相关内容
  • 一个类只能有一个析构函数。 无法继承或重载析构函数。 无法调用析构函数。它们是被自动调用的。 析构函数既没有修饰符,也没有参数。 例如,下面是类 Car 的析构函数的声明: class Car { ~Car() // destructor ...
  • C++析构函数 ...注意:析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。如果用户没有定义,那么编译器会自动生成。 析构函数举例: #include using namespace std; class Student{ p
  • C++虚析构函数和纯虚析构函数

    千次阅读 2020-07-05 10:31:05
    使用虚析构函数一般是要作为基类,被其他继承。通过把基类的析构函数声明为虚函数,就可以通过父类指针来释放子类对象,从而完成子类的一些清理工作,防止出现内存泄漏。 案例1:基类析构函数为非虚函数 //...

    1、为什么要使用虚析构函数

    我们知道析构函数是在对象生命周期结束时自动被调用,用来做一些清理工作(如释放句柄,释放堆内存等),防止出现内存泄漏。

    那怎么还有虚析构函数呢?

    使用虚析构函数的类一般是要作为基类,被其他类继承。通过把基类的析构函数声明为虚函数,就可以通过父类指针来释放子类对象,从而完成子类的一些清理工作,防止出现内存泄漏。

    案例1:基类析构函数为非虚函数

    //test.h
    class Parent
    {
    public:
            Parent();
            ~Parent();
    private:
            int *p_ptr;
    };
    
    class Child : public Parent
    {
    public:
            Child();
            ~Child();
    private:
            int *c_ptr;
    };
    
    //test.cpp
    #include "test.h"
    #include <iostream>
    
    using namespace std;
    
    Parent::Parent()
    {
            p_ptr=new int;
            *p_ptr=10;
    }
    
    Parent::~Parent()
    {
            cout << "Parent::~Parent() was called." << endl;
            if(p_ptr != 0)
            {
                    delete p_ptr;
                    p_ptr=0;
            }
    }
    
    Child::Child()
    {
            c_ptr=new int;
            *c_ptr=20;
    }
    
    //main.cpp
    #include "test.h"
    
    void func(Parent *parent)
    {
            delete parent;//通过父类指针来释放子类对象
    }
    
    int main(int argc, char *argv[])
    {
            Child *child=new Child;
    
            func(child);
            return 1;
    }
    

    运行结果:

    Parent::~Parent() was called.
    结论:父类析构函数为非虚函数时,通过父类指针来释放子类对象时,只会调用父类的析构函数,而不会调用子类的析构函数,造成了子类的内存泄漏。所以,应该将父类的析构函数声明为虚函数。

    案例2:父类的析构函数为虚函数

    其他文件不用动,只需修改test.h,将父类的析构函数声明为虚函数。

    //test.h
    class Parent
    {
    public:
            Parent();
            virtual ~Parent();//虚析构函数
    private:
            int *p_ptr;
    };
    
    class Child : public Parent
    {
    public:
            Child();
            ~Child();
    private:
            int *c_ptr;
    };
    
    

    运行结果:

    Child::~Child() was called.
    Parent::~Parent() was called.
    结论:只有将父类的析构函数声明为虚析构函数时,通过父类指针释放子类对象时,会先调用子类的析构函数,然后调用父类的析构函数,不存在内存泄漏问题。

    2、纯虚析构函数

    通过上面的虚析构函数知道,C++基类的析构函数最好声明为虚机构函数,那什么时候声明为纯虚析构函数呢?

    我们知道,带有纯虚函数的类为抽象类,不能被实例化,只能被子类继承,所以当我们设计一个基类为抽象类时,可以把析构函数声明为纯虚析构函数,这样基类就是抽象类了。

    注意:纯虚析构函数也要有函数体,用来做一些基类的清理工作,防止基类出现内存泄漏。

    展开全文
  • 析构函数(方法)作用和构造方法正好相反,是对象被销毁之前最后一个被对象自动调用的方法。是PHP5中新添加的内容作用是用于实现在销毁一个对象之前执行一些特定的操作,诸如关闭文件和释放内存等。 下面在通过具体...
  • 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++析构函数

    千次阅读 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之间的时间决定的。

     

    展开全文
  • 文章目录、构造函数1、构造函数的概念2、构造函数的定义3、默认构造函数与参构造函数4、静态构造函数5、静态构造函数和实例构造函数的使用二、析构函数三、构造函数与析构函数区别 、构造函数 首先,我们要...
  • 一个类只能定义一个析构函数 析构函数不能重载 析构函数和构造函数一样不能加return , 也不用加关键字void 语法 析构函数名标识符就是在类名标识符符号前面加 ~ 符号 // PERSON.h 头文件 #pragma once class ...
  • C++析构函数定义和使用

    千次阅读 2021-06-10 20:22:40
    析构函数(destructor)是一个特殊的成员函数,它的作用与构造函数相反,它的名字是类名的前面加一个“~”符号。在C++“~”是位“取反”运算符,从这一点可以想到:析构函数是与构造函数作用相反的函数。例如:...
  • —— 析构函数 ... 没有参数,也没有返回值,而且不能重载,因此一个类中只能有一个析构函数; 当撤销对象时,编译系统会自动调用析构函数; #include &lt;iostream&gt; using na...
  • C++ 析构函数

    2019-03-29 13:10:00
    一个类最多只能有一个析构函数析构函数不返回任何值,没有函数类型,也没有函数参数,因此它不能被重载。 构造函数可能多个,但析构函数只能有一个,就像人来到人世间,可能出生的环境家庭不同(重载构造函数)...
  • C++中析构函数的作用

    千次阅读 2021-02-01 17:30:47
    只能有一个析构函数,不 能重载。 如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构 函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时 会...
  • 指针只是指向了一个内存地址,但是当存内存取值的时候,系统不知道你要从当前指针指向的地址,取几个字节,指定了指针的类型后,系统就知道取几个字节了。 char类型取1个字节,short类型取2个字节,int类型去4个...
  • 析构函数(不能重载,没有参数,一个类只能有一个析构函数。如果没定义,编译器会自动生成一个析构函数:创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理...
  • C#析构函数

    千次阅读 2020-09-28 09:47:01
    析构函数 析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。...只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统...
  • 4 构造函数与析构函数【C++】

    千次阅读 2022-04-26 13:04:57
    4. 构造函数与析构函数)【C++】
  • 析构函数

    万次阅读 多人点赞 2018-08-01 19:45:58
    1)构造函数可以多个来构成重载,但析构函数只能有一个,不能构成重载 2)构造函数可以参数,但析构函数不能参数 3)与构造函数相同的是,如果我们没有显式的写出析构函数,那么编译器也会自动的给我们加上...
  • 这篇文章用于总结当析构函数是普通析构函数、虚析构函数、纯虚析构函数时,我们使用delete运算符删除一个指针对象时,析构函数什么情况发生;普通析构函数CBase是基类,CDerive是其子类,源码代码如下:class ...
  • c++构造函数及析构函数特性

    千次阅读 2017-10-02 18:10:27
    2、c++规定,每个必须有一个构造函数,没有构造函数,则无法创建对象。 3、c++规定,如果一个类没有提供任何的构造函数,那么c++编译器会提供一个默认的构造函数,且这个默认的构造函数是不带参数的构造函数,它...
  • c++之抽象的纯虚析构函数

    千次阅读 2020-01-06 10:51:36
    定义:在定义一个表达抽象概念的基类时,有时无法给出某些函数的具体实现方法,就可以将这些函数声明为纯虚函数。 2.抽象类 定义:声明了纯虚函数的类,都成为抽象类。 主要特点:抽象类只能作为基类来派生新类,不...
  • 析构函数和delete

    千次阅读 2020-07-21 17:54:32
    一个类只能有一个析构函数,很多时候都是系统隐式调用对对象进行销毁,程序员也无法显式调用,(但可以在析构函数里写上自己想要在销毁的时候需要进行的别的工作,比如: /用来计数雇员数量程序/ Employee(std::...
  • 析构函数的调用

    千次阅读 2020-10-07 21:22:41
    析构函数的调用
  • C++析构函数析构函数介绍:示例:参考链接     析构函数的另一个特殊成员函数...换言之,一个类可以多个构造函数,但是只能有一个析构函数析构函数介绍: 1.析构函数(destructor) 与构造函数相反,当对...
  • 将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。 C++默认的***析构函数不是虚函数是因为虚函数...
  • 析构函数的定义

    万次阅读 多人点赞 2017-12-07 22:02:33
    析构函数往往用来做“清理善后”的工作(例如在建立对象时用new开辟了段内存空间,则在该对象消亡前应在析构函数中用delete释放这段存储空间)。 C++规定析构函数的名字是类名的前面加一波浪号(~)。其定义形式...
  • 我是柠檬叶子C,本章将继续讲解C++的面向对象的知识点,本篇主要讲解默认成员函数的构造函数、析构函数和拷贝构造函数。还是和以前一样,我们将由浅入深地去讲解,以 "初学者" 的角度去探索式地学习。会步步地...
  • 在C++,对于一个类,C++的编译器都会为这个提供四个默认函数,分别是: A() //默认构造函数 ~A() //默认析构函数 A(const A&) //默认拷贝构造函数 A& operator = (const A &) //默认赋值函数。 ...
  • 当成员变量是私有的时候,要对它们初始化就必须用一个公有的成员函数来进行。同时这个成员函数应在仅且在对象定义时自动的执行一次,这个函数我们就叫做构造函数。2. 构造函数的特点(1)函数名与类名相同;(2)无...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 72,662
精华内容 29,064
热门标签
关键字:

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