C++ 析構函數

C++ 析構函數

設計一個類時,如何寫析構函數?
析構函數如果我們不寫的話,C++ 會幫我們自動的合成一個,就是說:C++ 會自動的幫我們寫一個析構函數。

很多時候,自動生成的析構函數可以很好的工作,但是一些重要的事蹟,就必須我們自己去寫析構函數。
析構函數和構造函數是一對。構造函數用於創建對象,而析構函數是用來撤銷對象。簡單的說:一個對象出生的時候,使用構造函數,死掉的時候,使用析構函數。

下面我們來做一個例子,看看:

#include <iostream>
#include <string>

using namespace std;

class NoName{
public:
    NoName():pstring(new std::string), i(0), d(0){}
private:
    std::string * pstring;
    int i;
    double d;
};

int main(){

    return 0;
}

像上面這個 NoName 類這樣的設計,類裏面有一個成員變量是指針(std::string *pstring) ,那麼在構造函數裏我們使用 new 創建了對象,並使用 pstring 來操作這個對象。那麼在這個情況下,我們就必須設計一個析構函數。

析構函數是這樣編寫的:(可以在類的裏面聲明,定義寫在類的外面,)

class NoName{
public:
    NoName():pstring(new std::string)
        , i(0), d(0){
        cout << "構造函數被調用了!" << endl;
    }
    ~NoName();
NoName::~NoName(){
    cout << "析構函數被調用了!" << endl;
}

析構函數是這樣寫的: ~NoName() ,它與構造函數唯一的區別就是,前面都加了一個 ~ 符號。 析構函數都是沒有參數的,也就是說:析構函數永遠只能寫一個。
按照 C++ 的要求,只要有 new 就要有相應的 delete 。這個 new 是在構造函數裏 new 的,就是出生的時候。所以在死掉的時候,就是調用析構函數時,我們必須對指針進行 delete 操作。

我們來在main() 函數中創建一個 NoName 實例對象來 測試一下:

int main(){
    NoName a;

    return 0;
}

並且在 main() 函數的 } 這一行添加一個斷點,如圖所示:

這裏寫圖片描述

運行輸出:

構造函數被調用了!
析構函數被調用了!

在定義 a 對象的時候,調用了 NoName 類的構造函數,在main() 函數執行完的時候,就是執行完 return 0; 這句話後,main() 函數的執行域結束,所以就要殺掉 a 對象,所以這個時候會調用 NoName 類的析構函數。

那麼,如果我們在 main() 函數中使用 new 關鍵字來創建一個 NoName 類的實例化對象,會出現什麼樣的運行效果呢?
main() 函數中的代碼如下:

int main(){
    NoName a;
    NoName *p = new NoName;

    return 0;
}

運行輸出:

構造函數被調用了!
構造函數被調用了!
析構函數被調用了!

這裏使用 new 創建的對象,就必須要使用 delete 來釋放它。 牢牢的記住:newdelete 是一對。正確的代碼是下面這個樣子的:

int main(){
    NoName a;
    NoName *p = new NoName;

    delete p;
    return 0;
}

運行輸出:

構造函數被調用了!
構造函數被調用了!
析構函數被調用了!
析構函數被調用了!

構造函數 和 析構函數 各有各的用途,在構造函數中,我們來獲取資源;在析構函數中,我們來釋放資源。釋放了之後,這些資源就會被回收,可以被重新利用。
比如說,我們在構造函數裏打開文件,在析構函數裏關閉打開的文件。這是一個比較好的做法。
在構造函數裏,我們去連接數據庫的連接,在析構函數裏關閉數據庫的連接。
在構造函數裏動態的分配內存,那麼在析構函數裏把動態分配的內存回收。

構造函數 和 析構函數 之間的操作是向對應的。
如果我們不寫析構函數,C++ 會幫我們寫一個析構函數。C++幫我們寫的這個析構函數只能做一些很簡單的工作,它不會幫助我們去打開文件、連接數據庫、分配內存這些操作,相應的回收,它也不會給我們寫。所以需要我們自己手動的寫。(如果要做這些操作,我們必須自己寫。)

如果我們自己寫了析構函數,記住三個原則:
如果你寫了析構函數,就必須同時寫賦值構造函數 和 賦值操作符。你不可能只寫一個。

在賦值的時候,不是講指針賦值過來,而是將指針對應指向的字符串賦值過來,這是最關鍵的一步。

因爲我們寫了析構函數,就必須要將賦值構造函數寫上:

class NoName{
public:
    NoName():pstring(new std::string)
        , i(0), d(0){
        cout << "構造函數被調用了!" << endl;
    }
    NoName(const NoName & other);
    ~NoName();
NoName::NoName(const NoName & other){
    pstring = new std::string;
    *pstring = *(other.pstring);
    i = other.i;
    d = other.d;
}

除了要寫 賦值構造函數,還要寫賦值操作符。

class NoName{
public:
    NoName():pstring(new std::string)
        , i(0), d(0){
        cout << "構造函數被調用了!" << endl;
    }
    NoName(const NoName & other);
    ~NoName();

    NoName& operator =(const NoName &rhs);12345678910
NoName& NoName::operator=(const NoName &rhs){
    pstring = new std::string;
    *pstring = *(other.pstring);
    i = other.i;
    d = other.d;
    return *this;
}

只要你寫了析構函數,就必須要寫 賦值構造函數 和 賦值運算符,這就是著名的 三法則 (rule of three



總結:

在設計一個類的時候,如果我們一個構造函數都沒有寫,那麼 C++ 會幫我們寫一個構造函數。只要我們寫了一個構造函數,那麼 C++ 就不會再幫我們寫構造函數了。

構造函數可以重載,可以寫很多個,析構函數不能重載,只能寫一個。如果我們沒有寫析構函數,C++會自動幫我們寫一個析構函數。那麼在工作的時候,我們寫的析構函數會被調用,調用完成之後,C++會執行它自動生成的析構函數。

如果我們寫的類是一個沒有那麼複雜的類,我們可以不需要寫析構函數。如果一個類只要有這些情況:打開文件、動態分配內存、連接數據庫。簡單的說:就是隻要構造函數裏面有了 new 這個關鍵詞,我們就需要自己手動編寫析構函數。

那麼如果我們寫了析構函數,就必須要注意三法則:同時編寫:析構函數、賦值構造函數、賦值運算符。

完整的代碼:

#include <iostream>
#include <string>

using namespace std;

class NoName{
public:
    NoName():pstring(new std::string)
        , i(0), d(0){
        cout << "構造函數被調用了!" << endl;
    }
    NoName(const NoName & other);
    ~NoName();

    NoName& operator =(const NoName &rhs);
private:
    std::string * pstring;
    int i;
    double d;
};

NoName::~NoName(){
    cout << "析構函數被調用了!" << endl;
}

NoName::NoName(const NoName & other){
    pstring = new std::string;
    *pstring = *(other.pstring);
    i = other.i;
    d = other.d;
}

NoName& NoName::operator=(const NoName &rhs){
    pstring = new std::string;
    *pstring = *(rhs.pstring);
    i = rhs.i;
    d = rhs.d;
    return *this;
}

int main(){
    NoName a;
    NoName *p = new NoName;

    delete p;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章