多載指定運算子

在前一章,我們學會了如何多載比較運算子,一種非常常用的運算子種類。

今天我們要來多載指定運算子 =,英文叫做 assignment operator,也叫做 copy assignment operator。

多載這個運算子讓我們可以客製化指定物件為某一個值的這個過程。

讓我們繼續看下去吧!

多載 =

因為這不是我們第一次多載運算子,因此,在這章我們就直接來看該如何多載 = 運算子吧!

這裡我們和之前一樣,使用 Dollars 作為我們的範例。

class Dollars
{
private:
    int m_dollars;

public:
    Dollars() {};
    Dollars(int dollars) : m_dollars{ dollars } {}
    Dollars& operator=(const Dollars& d);
    
    int getDollars() const { return m_dollars; }
};

Dollars& Dollars::operator=(const Dollars& d)
{
    m_dollars = d.getDollars();
    return *this;
}

int main() {
    Dollars d1{10};
    Dollars d2;
    
    d2 = d1;
    std::cout << d2.getDollars() << std::endl;

    return 0;
}

執行後,我們會看到這樣的輸出:

10

如果有好好讀完前面幾章的人,那你應該是非常清楚這樣的程式碼在做什麼~

這裡我就不介紹了,我們要來談一個更為重要的觀念!

指定運算子 v.s. 複製建構子

還不熟悉什麼是複製建構子的人,可以去看看這章 複製建構子 Copy Constructor

這兩個東西做的事情非常像,因此常常有很多初學者搞不清楚這兩者的區別。

這兩個東西其實都是在賦予物件某個值,但賦予的手段有些許不同。

  • 在複製建構子中,物件是先被創造,然後才被賦予值。
  • 在指定運算子中,沒有物件被創造,被賦予值的物件是本來就存在的

我們來看這個稍微複雜一點的例子:

class Dollars {
private:
    int m_dollars; // 美元金額
    std::string m_currency; // 貨幣單位

public:
    // 預設建構子
    Dollars() : m_dollars(0), m_currency("USD") {
        std::cout << "呼叫預設建構子" << std::endl;
    }

    // 參數化建構子
    Dollars(int dollars, const std::string& currency) : m_dollars(dollars), m_currency(currency) {}

    // 複製建構子
    Dollars(const Dollars& other) : m_dollars(other.m_dollars), m_currency(other.m_currency) {
        std::cout << "呼叫複製建構子" << std::endl;
    }

    // 複製指定運算子
    Dollars& operator=(const Dollars& other) {
        std::cout << "呼叫複製指派運算子" << std::endl;
        if (this != &other) {
            m_dollars = other.m_dollars;
            m_currency = other.m_currency;
        }
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& os, const Dollars& dollars) {
        os << "金額:" << dollars.m_dollars << " " << dollars.m_currency;
        return os;
    }
};

int main() {
    // 使用參數化建構子創建實例
    Dollars dollar1(100, "USD");
    std::cout << dollar1 << std::endl;

    // 使用複製建構子從 dollar1 創建 dollar2
    Dollars dollar2 = dollar1;
    std::cout << dollar2 << std::endl;

    // 使用複製指定運算子將 dollar2 的值複製到 dollar3
    Dollars dollar3;
    dollar3 = dollar2;
    std::cout << dollar3 << std::endl;

    return 0;
}

執行程式碼後,我們可以看到這樣的輸出:

金額:100 USD
呼叫複製建構子
金額:100 USD
呼叫預設建構子
呼叫複製指派運算子
金額:100 USD

可以看到,在將 dollar1 複製給 dollar2 時,我們是使用複製建構子 Copy Constructor。

複製建構子「先」創造了 dollar2,「再」將 dollar1 的值複製過去。

而在將 dollar2 複製給 dollar3 時,我們是使用複製指定運算子 Copy Assignment Operator。

這是因為物件 dollar3 原本就已經存在了,指定運算子只是將 dollar2 的值複製過去而已。

隱藏的指定運算子

和複製建構子一樣,如果我們不多載指定運算子,C++ 編譯器會暗中幫我們生成一個。

也就是說,如果刪掉這段:

// 複製指定運算子
Dollars& operator=(const Dollars& other) {
    std::cout << "呼叫複製指派運算子" << std::endl;
    if (this != &other) {
        m_dollars = other.m_dollars;
        m_currency = other.m_currency;
    }
    return *this;
}

程式碼依然可以執行!

總結

這篇我們又學到了一個多載運算子的實際應用啦!

如何多載指定運算子我相信如果你們有看前幾章的話那肯定不難,重要的是要能分清楚 copy assignment operator 和 copy constructor 這兩個概念,以及程式碼什麼時候呼叫哪個!

這對於想要深刻理解 C++ 的人來說非常重要,因為這會涉及到記憶體的處理。

那這篇就到這裡啦!有學到東西的話歡迎留五星評價喔!