建構子 Constructor

類別 Class 中,我們介紹了什麼是類別以及該如何創造一個類別,其中我們講到了建構子,但我們沒有細講什麼是建構子。

建構子是創造一個實體物件必備的要件之一。接下來,讓我們仔細來討論這個重要的要件吧!

什麼是建構子

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

當一個物件被創造時,編譯器會在該類別中尋找可以匹配的建構子。如果有找到可以匹配的建構子,那麼物件就會被創建。否則程式碼就會報錯。

在建構子中,通常有兩件事會被執行:

  1. 初始化類別中的參數
  2. 執行其他函數

初始化類別中的參數比較直觀,這邊就不贅述。

在執行其他函數中,常見的功能可能會是檢查參數值、讀取文件等等。這些步驟常常是物件創建時需要馬上執行的。

如何創建

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

  1. 建構子名稱必須和類別名稱相同
  2. 建構子沒有回傳值

也就是說,如果有一個類別叫做 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;
    }

    void print() const
    {
        std::cout << "Object(" << m_x << ", " << m_y << ")" << std::endl;
    }
};

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

    return 0;
}

在這個例子中,我們創建了一個叫做 Object 的類別,這個類別需要兩個參數才可以創立。

注意看這裡我們是如何初始化類別中的參數的。

Object(int x, int y) : m_x(x), m_y(y)

這個技巧叫做 Member Initializer List,單純是用來初始化類別參數的。

但是如果你想要寫在建構子本身內也可以達到同樣的效果,比如說:

Object(int x, int y)
{
    m_x = x;
    m_y = y;
}

但是在近代的 C++ 中,使用 Member Initializer List 才是比較推薦的用法,這是考慮到執行效能的因素。可以參考 Stack Overflow 這篇 Why should I prefer to use member initializer lists 的討論。

以上的程式碼在執行後會有這樣的輸出:

呼叫建構子:Object(3, 4)
Object(3, 4)
多個建構子

沒錯!一個類別可以有好幾個建構子!

為什麼?你想喔,類別是建造物件的範本,而我們可能會想讓這個物件擁有不同的特徵。

就好比 10 個人都訂購了馬自達 CX5 的車,雖然都是 CX5,但是每個人的要求可能不一樣。有的人可能會想要有天窗、有的人可能會想要車子是灰色的、有的人可能想要有加熱座椅。

不同的建構子就是讓我們可以用不同的方式建構出實體物件。

我們直接來看個例子:

class Object
{
private:
    int m_x;
    int m_y;

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

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

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

int main()
{
    Object obj1 = Object();
    Object obj2 = Object(3);
    Object obj3 = Object(3, 4);

    return 0;
}

在上面的例子中,我們可以看到有三個建構子,分別接收 0、1、2 個參數。

在主程式中,我們則利用不同的建構子建立了三個實體物件,我們可以從輸出看到的確不同的建構子被呼叫了!

呼叫建構子 Object()
呼叫建構子 Object(3)
呼叫建構子 Object(3, 4)

總結

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

然而建構子的概念遠遠不止這樣,更進階的還有 Copy Constructor、Copy Assignment Operator 等等。

但這章我們講大概念就行了!以後有空再講更進階的概念吧!

希望有讓你們學到東西囉~