精华内容
下载资源
问答
  • 3:类中只能存在一个析构函数; 4:析构函数为一个回调函数,在对象生命周期结束时,系统将自动调用; 5:若在类定义时显示提供了析构函数,则编译器不会再产生析构函数 注:当类中存在指针类型的数据成员,...

    作用 : 用于回收构造函数申请的内存资源。

    创建: ~classname();

    特殊性
    1:不允许有返回值(void也不行);
    2:无参函数;
    3:类中只能存在一个析构函数;
    4:析构函数为一个回调函数,在对象生命周期结束时,系统将自动调用;
    5:若在类定义时显示提供了析构函数,则编译器不会再产生析构函数
    注:当类中存在指针类型的数据成员,且在类对象实例化时,利用该指针动态申请了内存空间,必须显示提供的析构函数,且在析构函数中手动回收。

    展开全文
  • C++ 析构函数

    2019-11-03 10:49:04
    C++ 类 析构函数 阅读目录 一、析构函数的定义 二、析构函数的调用 ...一个类最多只能有一个析构函数。析构函数不返回任何值,没有函数类型,也没有函数参数,因此它不能被重载。 构造函数可能...

    C++ 类 析构函数

    阅读目录

    1. 一、析构函数的定义
    2. 二、析构函数的调用
    3. 三、构造函数和析构函数的调用情况
    4. 四、私有析构函数

     


    回到顶部

    一、析构函数的定义


    析构函数为成员函数的一种,名字与类名相同,在前面加‘~’没有参数和返回值在C++中“~”是位取反运算符。
    一个类最多只能有一个析构函数。析构函数不返回任何值,没有函数类型,也没有函数参数,因此它不能被重载。

    构造函数可能有多个,但析构函数只能有一个,就像人来到人世间,可能出生的环境家庭不同(重载构造函数),但最终都会死亡(析构函数)。

    class C
    {
    public:
        ~C ( ) { }
        ~C (int i) { }    // error C2524: “C”: 析构函数 必须有“void”参数列表
        // warning C4523: “C”: 指定了多个析构函数
    };
    

    析构函数对象消亡时即自动被调用。可以定义析构函数来在对象消亡前做善后工作,比如释放分配的空间等。
    如果定义类时没写析构函数,则编译器生成缺省析构函数。缺省析构函数什么也不做。如果定义了析构函数,则编译器不生成缺省析构函数。
    析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作。

    例:

    class A
    {
    private :
        char * p;
    public:
        A ( )
        {
            p = new char[10];
        }
        ~ A ( )
        {
            delete [] p;
        }
    };
    

    若A类没写析构函数,则在生成A对象后,new出来的内存空间未清除,可能造成内存泄露。

     

    在创建一类的对象数组时,对于每一个数组元素,都会执行缺省的构造函数。同样,对象数组生命期结束时,对象数组的每个元素的析构函数都会被调用。

    #include<iostream>
    using namespace std;
    unsigned count = 0;
    class A
    {
    public:
        A ( )
        {
            i = ++count;
            cout << "Creating A " << i <<endl;
        }
        ~A ( )
        {
            cout << "A Destructor called " << i <<endl;
        }
    private :
        int i;
    };
    int main( )
    {
        A ar[3];  // 对象数组
        return 0;
    }

    复制代码

     

    程序执行结果为:
    Creating A 1
    Creating A 2
    Creating A 3
    A Destructor called 3
    A Destructor called 2
    A Destructor called 1

    类似于栈的后进先出

     

    回到顶部

    二、析构函数的调用


    如果出现以下几种情况,程序就会执行析构函数:
    (1)如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
    (2)static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。
    (3)如果定义了一个全局对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。
    (4)如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。
    (5)调用复制构造函数后。 

    例:

    复制代码

    例4.35
    #include <iostream>
    using namespace std;
    class CMyclass
    {
    public:
        ~CMyclass( )
        {
            cout << "destructor" << endl;
        }
    };
    CMyclass obj;
    CMyclass fun(CMyclass sobj )
    {
        return sobj;   //函数调用返回时生成临时对象返回
    }
    void main( )
    {
        obj = fun(obj);  //函数调用的返回值(临时对象)被用过后,该临时对象析构函数被调用
    }

    复制代码

    程序执行结果为:
    destructor   // 形参和实参结合,会调用复制构造函数,临时对象析构
    destructor   // return sobj函数调用返回,会调用复制构造函数,临时对象析构
    destructor   // obj对象析构


    总之,在临时对象生成的时候会有构造函数被调用,临时对象消亡导致析构函数调用。

    回到顶部

    三、构造函数和析构函数的调用情况


    构造函数用于对对象中的变量赋初值,析构函数用于释放所定义的对象的所有内存空间。构造函数和析构函数都不需要用户调用的,构造函数在定义对象时自动调用,析构函数当对象的生存期结束的时候会自动的调用。一般来说,析构函数的调用顺序与构造函数相反。但对象存储类型可以改变析构函数的调用顺序。
    全局范围中定义的对象的构造函数在文件中的任何其他函数(包括main)执行之前调用(但不同文件之间全局对象构造函数的执行顺序是不确定的)。全局变量是需要在进入main()函数前即初始化的,所以在一个程序中一个全局变量的构造函数应该是第一个被调用的,比main()函数都要早同时全局对象又必须在main()函数返回后才被销毁,当main终止或调用exit函数时调用相应的析构函数,所以它的析构函数是最后才被调用。
    当程序执行到对象定义时,调用自动局部对象的构造函数。该对象的析构函数在对象离开范围时调用(即离开定义对象的块时)。自动对象的构造函数与析构函数在每次对象进人和离开范围时调用。
    static局部对象的构造函数只在程序执行首次到达对象定义时调用一次,对应的析构函数在main终止或调用exit函数时调用。

     

    复制代码

    #include <iostream>
    using namespace std;
    class A
    {
    public:
        A( int value )
        {
            i = value;
            cout << "Object "<<i<<" constructor";
        }
        ~A()    // destructor
        {
            cout <<"Object "<<i<<" destructor"<< endl;
        }
    private:
        int i;
    };
    A first(1);  // global object全局变量
    void func()
    {
        A fifth(5);
        cout <<" (local automatic in create)"<< endl;
        static A sixth( 6 );
        cout <<" (local static in create)"<< endl;
        A seventh(7);
        cout <<" (local automatic in create)"<< endl;
    }
    int main()
    {
        cout <<" (global created before main)" <<endl;
        A second(2);
        cout <<" (local automatic in main)"<< endl;
        static A third(3);  // local object
        cout <<" (local static in main)"<< endl;
        func();     // call function to create objects
        A fourth(4);      // local object
        cout <<" (local automatic in main)"<< endl;
        return 0;
    }

    复制代码

     

    程序执行结果为:
    Object 1 constructor (global creasted before main)
    Object 2 constructor (local automatic in main)
    Object 3 constructor (local static in main)
    Object 5 constructor (local automatic in create)
    Object 6 constructor (local static in create)
    Object 7 constructor (local automatic in create)
    Object 7 destructor
    Object 5 destructor
    Object 4 constructor (local automatic in main}
    Object 4 destructor
    Object 2 destructor
    Object 6 destructor
    Object 3 destructor
    Object 1 destructor

    上例中,main函数中声明3个对象,对象second和fourth是局部自动对象,对象third是static局部对象。这些对象的构造函数在程序执行到对象定义时调用。对象fourth和second的析构函数在到达main函数结尾时候依次调用。由于对象third是static局部对象,因此到程序结束时才退出,在程序终止时候删除所有其他对象之后和调用first的析构函数之前调用对象third的析构函数。函数func声明3个对象。对象fifth和seventh是局部自动对象,对象sixth是static局部对象。对象seventh和fifth的析构函数在到func结束时候依次调用。由于对象sixth是static局部对象,因此到程序结束时才退出。sixth的析构函数在程序终止时删除所有其他对象之后和调用third和first的析构函数之前调用。

     

     

    若函数参数是类类型,调用函数时要调用复制构造函数,用实际参数初始化形式参数。当函数返回类类型时,也要通过复制构造函数建立临时对象。

    例:

    复制代码

    #include <iostream>
    using namespace std;
    class A
    {
    public:
        A()
        {
            cout<<"A constructor"<<endl;
        }
        ~A()
        {
            cout<<"A destructor"<<endl;
        }
        A(A &)
        {
            cout <<"A copy constructor"<<endl;
        }
    };
    class B
    {
    public:
        B()
        {
            cout<<"B constructor"<<endl;
        }
        ~B()
        {
            cout<<"B destructor"<<endl;
        }
        B(B &)
        {
            cout<<"B copy constructor"<<endl;
        }
    };
    A a;
    B b;
    void func1(A obj) {}
    void func2(B &obj) {}
    int main()
    {
        func1(a);
        func2(b);
        return 0;
    }

    复制代码

    程序执行结果为:
    A constructor       // a构造
    B constructor       // b构造
    A copy constructor   // func1函数参数调用A的复制构造函数
    A destructor   // func1函数参数析构
    B destructor   // b析构
    A destructor   // a析构

    上例中函数func1的参数是A的值传递方式,实参初始化形参时需要调用复制构造函数,函数调用结束后,栈上的形参消亡时要调用A的析构函数。函数func2的参数是B的引用传递方式,形参只是实参的一个别名,并没有创建新的对象,因此不会调用复制构造函数和析构函数。

     

    这里再附上一道面试题:

    复制代码

    #include <iostream>
    using namespace std;
    void hello( )
    {
        cout << "  Hello, world!\n";
    }
    int main( )
    {
        hello( );
        return 0;
    }

    复制代码

     

    试修改上面的程序,使其输出变成:
     Begin
       Hello, world!
     End
    限制:(1)不能对main()进行任何修改;(2)不能修改hello()函数。
    这里用到了构造函数和析构函数的调用了。

    复制代码

    #include <iostream>
    using namespace std;
    class A {
    public:
      A ( ) { cout << "Begin\n"; }
      ~A ( ) { cout << "End\n"; }
    };
    
    void hello( ) {cout << "  Hello, world!\n"; }
    
    A a;      // a是一个全局对象
    int main( ) {
      hello( ); 
      return 0;
    }

    复制代码

     

     

     

    下面总结下不同存储类型构造函数和析构函数的调用。


    构造函数的调用:
    (1)全局变量:程序运行前;
    (2)函数中静态变量:函数开始前;
    (3)函数参数:函数开始前; 
    (4)函数返回值:函数返回前;
    析构函数的调用:
    (1)全局变量:程序结束前;
    (2)main中变量:main结束前;
    (3)函数中静态变量:程序结束前;
    (4)函数参数:函数结束前;
    (5)函数中变量:函数结束前;
    (6)函数返回值:函数返回值被使用后;

     对于相同作用域和存储类别的对象,调用析构函数的次序正好与调用构造函数的次序相反 
     

    回到顶部

    四、私有析构函数


    有时你想这样管理某些对象,要让某种类型的对象能够自我销毁,也就是能够“delete this”。很明显这种管理方式需要此类型对象被分配在堆中。若我们必须在堆中创建对象,为了执行这种限制,你必须找到一种方法禁止以调用“new”以外的其它手段建立对象。这很容易做到,非堆对象(non-heap object),即栈对象在定义它的地方被自动构造,在生存时间结束时自动被释放,所以只要禁止使用隐式的析构函数,就可以实现这种限制。
    把这些调用变得不合法的一种最直接的方法是把析构函数声明为private,把构造函数声明为public。你可以增加一个专用的伪析构函数,用来访问真正的析构函数,客户端调用伪析构函数释放他们建立的对象。

     

    复制代码

    #include <iostream>
    using namespace std;
    class A
    {
    public:
        A()
        {
            cout<<"A"<<endl;
        }
        void destroy() const
        {
            cout<<"delete A"<<endl;
            delete this;
        }
    private:
        ~A() {}
    };
    int main( )
    {
        A *pa = new A;
        pa->destroy();
        return 0;
    }

    复制代码

     

    程序执行结果为:
            A
            delete A
    若A类对象是在栈上创建:
        A a;  // error C2248: “A::~A”: 无法访问 private 成员(在“A”类中声明)
    编译时会提示不能访问私有成员。因为在栈上生成对象时,类对象在离开作用域时会调用析构函数释放空间,此时无法调用私有的析构函数。C++在编译过程中,所有的非虚函数调用都必须分析完成。即使是虚函数,也需检查可访问性。因此,当在栈上生成对象时,对象会自动析构,也就说析构函数必须可以访问。而堆上生成对象,由于析构时机由程序员控制,所以不一定需要析构函数。

     

    被声明为私有析构函数的类对象只能在堆上创建,并且该类不能被继承。
     

    复制代码

    class A
    {
    private:
        ~A() {}
    };
    class B : public A
    {
        // error C2248: “A::~A”: 无法访问 private 成员(在“A”类中声明)
    }  // warning C4624: “B”: 未能生成析构函数,因为基类析构函数不可访问

    复制代码

     

    作者:王陸

    出处:http://www.cnblogs.com/wkfvawl/

    -------------------------------------------

    个性签名:罔谈彼短,靡持己长。做一个谦逊爱学的人!

    本站使用「署名 4.0 国际」创作共享协议,转载请在文章明显位置注明作者及出处。“推荐”哦,博主在此感谢!

     

    分类: C++面向对象程序设计

    展开全文
  • 析构函数(不能重载,没有参数,一个类只能有一个析构函数。如果没定义,编译器会自动生成一个) 析构函数:创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理...
    • 析构函数(不能重载,没有参数,一个类只能有一个析构函数。如果没定义,编译器会自动生成一个)

    析构函数:创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作。

    析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个“~”符号。 如下函数:

    #include <iostream>
    using namespace std;
    
    class Func{
    public:
        Func(int len);  //构造函数
        ~Func();  //析构函数
    public:
        void input();  //从控制台输入数组元素
        void show();  //显示数组元素
    private:
        int *ptr(int i);  //获取第i个元素的指针
    private:
        const int m_len;  //数组长度
        int *m_arr; //数组指针
        int *m_p;  //指向数组第i个元素的指针
    };
    
    Func::Func(int len): m_len(len){
        if(len > 0)
    	{ 
    		m_arr = new int[len];  /*分配内存*/ 
    	}
        else
    	{ 
    		m_arr = NULL; 
    	}
    }
    Func::~Func()
    {
        delete[] m_arr;  //释放内存
    }
    void Func::input()
    {
        for(int i=0; m_p=ptr(i); i++)	
    		cin>>*ptr(i); 
    }
    void Func::show()
    {
        for(int i=0; m_p=ptr(i); i++)
    	{
            if(i == m_len - 1)
    		{ 
    			cout<<*ptr(i)<<endl; 
    		}
            else
    		{ 
    			cout<<*ptr(i)<<", "; 
    		}
        }
    }
    int * Func::ptr(int i)
    {
        if(!m_arr || i<0 || i>=m_len)
    	{ 
    		return NULL; 
    	}
        else
    	{ 
    		return m_arr + i; 
    	}
    }
    
    int main(){
        //创建一个有n个元素的数组(对象)
        int n;
        cout<<"Input array length: ";
        cin>>n;
        Func *parr = new Func(n);
        //输入数组元素
        cout<<"Input "<<n<<" numbers: ";
        parr -> input();
        //输出数组元素
        cout<<"Elements: ";
        parr -> show();
        //删除数组(对象)
        delete parr;
    
        return 0;
    }

    在所有函数之外创建的对象是全局对象,它和全局变量类似,位于内存分区中的全局数据区,程序在结束执行时会调用这些对象的析构函数。

    在函数内部创建的对象是局部对象,它和局部变量类似,位于栈区,函数执行结束时会调用这些对象的析构函数。
    new 创建的对象位于堆区,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。(下面的例子)

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Demo{
    public:
        Demo(string s);
        ~Demo();
    private:
        string m_s;
    };
    Demo::Demo(string s): m_s(s){ }
    Demo::~Demo(){ cout<<m_s<<endl; }
    
    void func(){
        //局部对象
        Demo obj1("1");
    }
    
    //全局对象
    Demo obj2("2");
    
    int main(){
        //局部对象
        Demo obj3("3");
        //new创建的对象
        Demo *pobj4 = new Demo("4");
        func();
    	/*位置1*/
    	//delete(pobj4);//有delete 才会调用 pobj4
        cout<<"main"<<endl;
    	/*位置2*/
    	delete(pobj4);//位置不同调用的时机也不同
        
        return 0;
    }

    运行结果分析:

    展开全文
  • 而如果一个类并不用作基类,则没必要把析构函数声明为虚的,否则会浪费空间来存储虚函数指针和虚函数表。STL容器并不含虚析构函数,因此最好不要继承STL容器。只要有一个纯虚函数,就会导致抽象基类,即不能声明该...
    使用一个基类指针删除派生类对象,同时基类的析构函数是非虚的,那么这个指针只会删除派生类对象中的基类的部分,其派生部分不会被删除。造成资源泄漏。

    而如果一个类并不用作基类,则没必要把析构函数声明为虚的,否则会浪费空间来存储虚函数指针和虚函数表。

    STL容器并不含虚析构函数,因此最好不要继承STL容器。

    只要有一个纯虚函数,就会导致抽象基类,即不能声明该基类的对象。抽象基类只能用来继承,纯虚函数如:
    1. virtual ~AWOV() = 0;
    你必须为这个纯虚析构函数提供定义,因为析构函数调用时,最先调用派生类的,然后才调用基类的。编译器会在派生类的析构函数中创建一个对基类析构函数的调用,所以必须提供一份定义。否则链接器会发出抱怨。与之相反的是:构造函数调用时先调用基类的,再构造派生部分的。





    转载于:https://www.cnblogs.com/Lighters-c/p/6080465.html

    展开全文
  • 析构函数

    2015-06-08 23:07:00
    析构函数是反向的构造函数,析构函数不允许返回值,不能带参数,一个类中只能存在一个析构函数 析构函数的作用于构造函数正好相反,对象超出其作用范围,内存空间就要被析构函数回收 根据这些特点,我们在构...
  • C#析构函数

    2019-10-04 21:57:43
    一、C#析构函数 1. 析构函数的定义与注意的问题 ...一个类只能有一个析构函数。 ?不能继承或重载析构函数。 ?析构函数只能被自动调用。 ?析构函数没有任何修饰符、没有任何参数、也不返回任何值。 2. ...
  • C++析构函数 ...注意:析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。如果用户没有定义,那么编译器会自动生成。 析构函数举例: #include using namespace std; class Student{ p
  • 一个类只能有一个析构函数。 无法继承或重载析构函数。 无法调用析构函数。它们是被自动调用的。 析构函数既没有修饰符,也没有参数。 例如,下面是类 Car 的析构函数的声明: class Car { ~Car() // destructor ...
  • 一、C#析构函数 1. 析构函数的定义与注意的问题...一个类只能有一个析构函数。 ?不能继承或重载析构函数。 ?析构函数只能被自动调用。 ?析构函数没有任何修饰符、没有任何参数、也不返回任何值。 2. 调用析构函...
  • C++中析构函数的作用

    2021-02-01 17:30:47
    只能有一个析构函数,不 能重载。 如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构 函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时 会...
  • 析构函数,构造函数

    2015-10-20 10:31:00
    使用析构函数释放资源 ...2)一个类只能有一个析构函数。 3)无法继承或重载析构函数。 4)无法调用析构函数。它们是被自动调用的。 5)析构函数既没有修饰符,也没有参数。 例如,下面是类Car的析...
  • 析构函数只能有一个)3.malloc、free、new、delete 区别4. 拷贝构造函数和调用场景 知识点: 面试题:finalize , finally, final 之间的区别 finalize : java垃圾回收回收该对象的时候会调用的方法 (c 析构...
  • (二)析构函数析构函数的...②不返回任何值,没有函数类型,也没有函数参数,由于无函数参数,则不能被重载,一个类只能有一个析构函数,但可以有多个构造函数。 ③一般情况下,在声明类的同时就定义析构函数。 ...
  • · 一个类只能有一个析构函数。 · 无法继承或重载析构函数。 · 无法调用析构函数。它们是被自动调用的。 · 析构函数既没有修饰符,也没有参数。 析构函数(destructor) 与构造函数相反,当对象脱离其作用域...
  • C++析构函数

    2020-01-30 19:44:27
    析构函数 一、作用 在对象的声生命周期结束时,做一些清理工作,列如:释放给对象分配的内存空间。 二、定义 ~类名() { 函数体 ...在一个类中只能有一个析构函数 撤销对象时,编译器自动调用 ...
  • C# 析构函数

    2011-07-06 15:05:16
    不能在结构定义析构函数。只能对类使用析构函数。 一个类只能有一个析构函数。 无法继承或重载析构函数。 无法调用析构函数。它们是被自动调用的。 析构函数既没有修饰符,也没有参数。
  • C#成员-析构函数

    千次阅读 2009-09-28 15:00:00
    ·一个类只能有一个析构函数。 ·无法继承或重载析构函数。 ·无法调用析构函数。它们是被自动调用的。 ·析构函数既没有修饰符,也没有参数例如,下面是类 Car 的析构函数的声明:class Car{ ~Car() //
  • 析构函数与构造函数

    2013-12-09 23:46:52
    构造函数: 1.构造函数最重要的作用是创建对象本身。...2.析构函数是”反向“的构造函数,析构函数不允许有返回值,更重要的是析构函数不允许带参数,并且一个类中只能有一个析构函数。 3.构造函数的作用正好与
  • · 一个类只能有一个析构函数。 · 无法继承或重载析构函数。 · 无法调用析构函数。它们是被自动调用的。 · 析构函数既没有修饰符,也没有参数。 例如,下面是类 Car 的析构函数的声明: class Car { ~...
  • 05.析构函数

    2016-11-04 19:27:00
    一个类只能有一个析构函数.(3).析构函数是不能被继承和重载的(4).析构函数是无法被手动调用,它只能自动调用(5).析构函数既没有修饰符,也没有参数语法:~类名(){}析构函数什么时候会被执行呢?当程序结束的时候,析构...
  • C#析构函数 (转载)

    2019-10-06 20:50:23
    一、C#析构函数 1. 析构函数的定义与注意的问题析...一个类只能有一个析构函数。 不能继承或重载析构函数。 析构函数只能被自动调用。 析构函数没有任何修饰符、没有任何参数、也不返回任何值。 2. 调用...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 238
精华内容 95
关键字:

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