c++ 操作符operator的重載

一個模擬Point點的類,內置函數類型爲int.

實現默認構造,自定義構造,拷貝構造、移動構造,拷貝賦值、移動賦值
重載成員運算符函數operator+=”、“operator+”、“operator<<
重載非成員運算法函數operator*”

1、類的聲明

class TestOperatorPoint {
public:
    // default
    TestOperatorPoint() = default;

    TestOperatorPoint(int x, int y) : 
        x(x), y(y) //參數名、成員變量可以同名
    {}


    // copy constructor
    TestOperatorPoint(const TestOperatorPoint& pt);
    // move constructor
    TestOperatorPoint(TestOperatorPoint&& pt);
    //// 錯誤的拷貝賦值
    //TestOperatorPoint operator=(const TestOperatorPoint& pt);
    // copy assign
    TestOperatorPoint& operator=(const TestOperatorPoint& pt);
    // move assign
    TestOperatorPoint& operator=(TestOperatorPoint&& pt);

	////////////////// operations
    TestOperatorPoint operator+(const TestOperatorPoint& pt)
    TestOperatorPoint& operator+=(const TestOperatorPoint& pt);

    //  輸出重載(友元函數)
    friend std::ostream& operator<< (std::ostream& out, const TestOperatorPoint& pt);


//private:
    int x, y;
};

2、類的具體實現

2.1 構造函數和賦值函數

以下類定義時的實現,類定義外實現是需要加類作用域。

    // copy constructor
    TestOperatorPoint(const TestOperatorPoint& pt)
    {
        std::cout << "copy constructor" << std::endl;
        x = pt.x;
        y = pt.y;
    }

    // move constructor
    TestOperatorPoint(TestOperatorPoint&& pt)
    {
        std::cout << "move constructor" << std::endl;
        x = std::move(pt.x);  // x,y是基本類型,移動時不影響
        y = std::move(pt.y);
    }

    //// 錯誤的拷貝賦值
    //TestOperatorPoint operator=(const TestOperatorPoint& pt)
    //{
    //    std::cout << "wrong copy assign" << std::endl;
    //   // x = pt.x;
    //    //y = pt.y;
    //   // return *this;   // 如果不返回引用,會將*this對象進行一次拷貝賦值,
    //    return pt;       // 直接返回也會再進行一次拷貝賦值,
    //}

    // copy assign
    TestOperatorPoint& operator=(const TestOperatorPoint& pt)
    {
        std::cout << "copy assign" << std::endl;
        x = pt.x;
        y = pt.y;
        return *this;   
    }

    // move assign
    TestOperatorPoint& operator=(TestOperatorPoint&& pt)
    {
        std::cout << "move assign" << std::endl;
        x = std::move(pt.x);  // x,y是基本類型,移動時不影響; 
        y = std::move(pt.y);
        return *this;
    }

需要說明的是,當成員變量是非基本類型,如string、vector、指針對象以及其他類對象時,move的作用在於減少過多的構造、賦值操作。
例如,如下操作

string str1 = "abc";
string str2 = std::move(str1)

結果是 str1爲"",str2爲"abc"。 str2進行的是移動構造。


測試代碼

TestOperatorPoint pt1;
TestOperatorPoint pt2(3, 4);   //自定義構造

TestOperatorPoint pt3(pt2);   // 拷貝構造
pt1 = pt2;                    // 拷貝賦值,   pt1已經存在


TestOperatorPoint pt4;
TestOperatorPoint pt5(1, 1);
TestOperatorPoint pt6(2, 2);

TestOperatorPoint pt7(std::move(pt5));   // 移動構造
pt4 = std::move(pt6);                    // 拷貝賦值,   pt4已經存在

2.2 類成員操作符重載(包括友元函數)

  • 賦值(=)、下標([ ])、調用( ( ) )和成員訪問箭頭( -> )必須是成員函數;
  • 改變對象狀態、遞增遞減、解引用等與自身類型密切相關的運算符,通常是成員函數;
	////////////////// operations

    TestOperatorPoint operator+(const TestOperatorPoint& pt)
    {
        TestOperatorPoint tmp;
        tmp.x = x + pt.x;
        tmp.y = y + pt.y;
        return tmp;     // 返回局部臨時變量,編譯器優化爲 移動操作,不要畫蛇添足
    }

    TestOperatorPoint& operator+=(const TestOperatorPoint& pt) 
    {
        x += pt.x;
        y += pt.y;
        return *this;   // 明確是對自己要修改,要返回引用。否則多一次拷貝構造。
    }


操作符重載的測試代碼,如下

TestOperatorPoint pt1(1, 2);
TestOperatorPoint pt2(3, 4);

// 移動構造(先執行pt1的操作符重載成員函數,返回臨時變量進行移動構造)
TestOperatorPoint pt3 = pt1 + pt2; 


TestOperatorPoint pt4(0,0);
pt4 += pt1;  // 僅調用pt4的+=函數

2.3 普通的非成員操作符重載

  • 算術、相等、關係和位運算,以及可以轉換任意一端的運算符對象,應該定義爲普通的普通的非成員操作符重載。
  • 輸入輸出運算符必須是普通的非成員函數。

2.3.1 operator*(const TestOperatorPoint& pt, T v)和operator*(T v,const TestOperatorPoint& pt)

例如,一個TestOperatorPoint對象,僅想將其成員變量x,y同時擴大一個倍數(可以是int, float, double等非類內置類型int),這種情況可以定義爲

// 混合類型表達式定義爲普通的非成員函數(第二個參數不是同一個類類型)
template<typename T>
TestOperatorPoint operator*(const TestOperatorPoint& pt, T v)
{
    TestOperatorPoint tmp;
    tmp.x = pt.x * v;     // 會強轉爲TestOperatorPoint.x的成員類型
    tmp.y = pt.y * v;
    return tmp;
}

測試使用 TestOperatorPoint pt5 = pt4 * 3.14;,是沒有問題;

但是如果我們先交換乘法順序(對稱性),如TestOperatorPoint pt6 = 3.14 * pt4; 這裏是無法執行的,因爲 3.14double類型,是沒有定義重載operator*(TestOperatorPoint)函數的,因此我們需要定義如下普通操作符函數

// 上面乘法運算的重載,對稱性
template<typename T>
TestOperatorPoint operator*(T v, const TestOperatorPoint& pt)
{
    TestOperatorPoint tmp;
    tmp.x = pt.x * v;     // 會強轉爲TestOperatorPoint.x的成員類型
    tmp.y = pt.y * v;
    return tmp;
}

此時,TestOperatorPoint pt6 = 3.14 * pt4;能夠正常執行。

2.3.2 重載輸入操作符函數 operator<<

通常自定義打印輸出的<<函數時,需要訪問類的非公有數據成員,因此定義爲友元函數。若不需要,可以定義爲非友元函數。

自定義打印輸入重載運算符。

    //  輸出重載(定義時實現)
    friend std::ostream& operator<< (std::ostream& out, const TestOperatorPoint& pt);
    {
        out << "[" << pt.x << ", " << pt.y << "]";
        return out;
    }

當在外部實現友元函數時,函數不要加類的作用域。

std::ostream& operator<< (std::ostream& out, const TestOperatorPoint& pt)
{
    out << "[" << pt.x << ", " << pt.y << "]";
    return out;
}

例如測試,std::cout << pt4 << std::endl;

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