解構子 Destructor

建構子 那篇中,我們知道建構子的存在是用來定義實體物件的創建過程。

既然有東西定義創建過程,那有沒有東西定義物件的銷毀過程呢?

有的!那就是我們今天要講的解構子 Destructor!

什麼是解構子

和建構子一樣,我們可以把解構子看作是一個類別中特別的函數,這個特別的函數會在物件被銷毀時「自動」執行。

你可能會問說,為什麼物件被銷毀時還要特別呼叫解構子這個函數?幹嘛不直接銷毀就好?

舉例來說,你可能會想在物件被銷毀之前,知道物件的當前狀態。或是在物件被銷毀之前,發送通知給使用者。

這些都可以透過解構子做到。

如何創建

建立解構子其實很簡單,有三個準則需要遵守。

  1. 解構子名稱必須和類別名稱相同,並且前面要有 ~ 符號
  2. 解構子沒有輸入參數
  3. 解構子沒有回傳值

也就是說,如果有一個類別叫做 Object,那麼這個類別跟他的解構子就會長這樣:

class Object
{
    ~Object() {}
};

我們可以注意到建構子的本體,也就是大括弧 {} 中沒有任何 return ,並且函數 ~Object() 前也沒有定義回傳型態。

實際範例

了解了解構子的意義以及如何創建後,我們來看幾個實際範例

簡單範例
class Object
{
private:
    int m_x;
    int m_y;

public:
    Object(int x, int y) : m_x(x), m_y(y)
    {
        std::cout << "呼叫建構子:Object(" << x << ", " << y << ")" << std::endl;
    }

    ~Object()
    {
        std::cout << "呼叫解構子:~Object()" << std::endl;
    }
};

int main()
{
    Object obj = Object(3, 4);

    return 0;
}

這裡的物件 obj 什麼時候會被銷毀呢?就是在 main() 函數結尾的時候會被銷毀。

所以,obj 的解構子 Object() 會在那時候被呼叫。

我們也可以透過印出來的訊息看到解構子確實被呼叫:

呼叫建構子:Object(3, 4)
呼叫解構子:~Object()
創建多個物件

在這個例子中我們可以看到建構子和解構子在多個物件創建時被自動呼叫的過程

class Object
{
private:
    int m_x;
    int m_y;

public:
    Object(int x, int y) : m_x(x), m_y(y)
    {
        std::cout << "呼叫建構子:Object(" << x << ", " << y << ")" << std::endl;
    }

    ~Object()
    {
        std::cout << "呼叫解構子:~Object()" << std::endl;
    }
};

int main()
{
    Object obj1 = Object(1, 2);
    Object obj2 = Object(3, 4);
    Object obj3 = Object(5, 6);

    return 0;
}

我們可以看到建構子和解構子分別都被呼叫了三次:

呼叫建構子:Object(1, 2)
呼叫建構子:Object(3, 4)
呼叫建構子:Object(5, 6)
呼叫解構子:~Object()
呼叫解構子:~Object()
呼叫解構子:~Object()

總結

這章我們了解了什麼是解構子 Destructor,它的作用以及該如何運作。

解構子的概念相對而言比建構子容易許多,但卻相當重要,也常常是記憶體出錯的關鍵原因之一。

那這章就到這裡啦!