C++之類的析構函數


 一、認識析構函數    

     在我的前一篇博客介紹了類的構造函數。http://www.cnblogs.com/MrListening/p/5557114.html。

     這里我們來簡單說說類的析構函數,它是類的一個成員函數,名字由波浪號加類名構成。看它的名字我們大概就能聯想到他功能,是執行與構造函數相反的操作:釋放對象使用的資源,並銷毀非static成員。

   同樣的,我們來看看析構函數的幾個特點:

1.函數名是在類名前加上~,無參數且無返回值。

2.一個類只能有且有一個析構函數,如果沒有顯式的定義,系統會生成一個缺省的析構函數(合成析構函數)。

3.析構函數不能重載。每有一次構造函數的調用就會有一次析構函數的調用。

      

拿程序說話:

 

//by Mr_Listening,06 08 2016  
class Date
{
public:
    Date(int year=1990,int month=1,int day=1)
        : _month(year), _year(month), _day(day)
    { }
    ~Date()
    {
        cout << "~Date()" << this << endl;
    }
private:
    int _year=1990;   
    int _month;
    int _day;
};
void test()
{
    Date d1;
}
int main()
{
    test();
    return 0;
}

 

      在test()函數中構造了對象d1,那么在出test()作用域d1應該被銷毀,此時將調用析構函數,下面是程序的輸出。當然在構建對象時是先調用構造函數的,在這里就不加以說明了。

           

      我們知道,在構造函數中,成員的在初始化是在函數體執行前完成的,並按照成員在類中出現的順序進行初始化,而在析構函數中,首先執行函數體,然后再銷毀成員,並且成員按照初始化的逆序進行銷毀。

二、銷毀,清理?

      我們一直在說析構函數的作用是在你的類對象離開作用域后釋放對象使用的資源,並銷毀成員。那么到底這里所說的銷毀到底是什么?那么繼續往下看:

 

  void test ()

 

   {

 

       int a=10;

 

       int b=20;

 

   }

 

      回想我們在一個函數體內定義一個變量的情況,在test函數中定義了a和b兩個變量,那么在出這個函數之后,a和b就會被銷毀(棧上的操作)。那么如果是是一個指向動態開辟的一塊空間的指針,我們都知道需要自己進行free,否則會造成內存泄漏。

      說到這里,其實在類里面的情況和這是一樣的,這就是合成析構函數體為空的原因,函數並不需要做什么,當類對象出作用域時系統會釋放你的內置類型的那些成員。但是像上面說的一樣,如果,我的成員里有一個指針變量並且指向了一塊你動態開辟的內存,那么像以前那樣也需要自己來釋放,此時就需要在析構函數內部寫你的釋放代碼,這樣在調用析構函數的時候就可以把你所有的資源進行釋放。(其實這才是析構函數有用的地方,對嗎)

那么還有一點,當類類型對象的成員還有一個類類型對象,那么在析構函數里也會調用這個對象的析構函數。

三、析構函數來阻止該類型對象被銷毀?

       我們如果不想要析構函數來對對象進行釋放該怎么做呢,不顯式的定義顯然是不行的,因為編譯器會生成默認的合成析構函數。之前我們知道了如果想讓系統默認生成自己的構造函數可以利用default,那么其實還有一個東西叫做delete。

    

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

     如果我這么寫了,又在底下創建Date類型的對象,那么這個對象將是無法被銷毀的,其實編譯器並不允許這么做,直接會給我們報錯。

   

        但其實是允許我們動態創建這個類類型對象的,像這樣:Date* p = new Date;雖然這樣是可行的,但當你delete p的時候依然會出錯,原因就不用說了吧

所以既然這樣做的話既不能定義一個對象也不能釋放動態分配的對象,所以還是不要這么用為好嘍。

 四、注意嘍

        一般在你顯式的定義了析構函數的情況下,應該也把拷貝構造函數和賦值操作顯式的定義。為什么呢??

       看下面的改動:

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

  成員中有動態開辟的指針成員,在析構函數中對它進行了delete,如果不顯式的定義拷貝構造函數,當你這樣:Date d2(d1)來創建d2,我們都知道默認的拷貝構造函數是淺拷貝,那么這么做的結果就會是d2的成員p和d1的p是指向同一塊空間的,呢么調用析構函數的時候回導致用一塊空間被釋放兩次,程序會崩潰的哦!

     

最后,

    我們知道析構函數是可以調用的,那么構造函數可不可以呢?是怎樣的使用環境


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM