前言:
在上文我們知道數據類型分為自定義類型和內置類型,當我想用內置類型比較大小是非常容易的但是在C++中成員變量都是在類(自定義類型)里面的,那我想給類比較大小那該怎么辦呢?這時候運算符重載就出現了
一 運算符重載概念:
允許用戶為自定義類定義或重新定義運算符的行為,使這些類在使用運算符時表現得像內置數據類型一樣,從而提高代碼的可讀性和簡潔性。
1.2 運算符重載與函數重載的區別:
我第一次聽到這兩個重載都傻傻的分不清楚,以為是一個意思。其實他們的區別可大了
函數重載:
函數重載是指在同一作用域中有多個同名函數,但它們的參數列表(參數的類型和數量)不同。編譯器通過參數列表來決定調用哪個函數。函數重載的目的是為了提高代碼的可讀性和靈活性,使同一操作可以應用于不同類型的參數。
運算符重載:
運算符重載允許我們為用戶定義的類型(如類)定義或重新定義特定運算符(如 +、-、*、/ 等)的行為。運算符重載函數的名稱為 operator
后跟運算符符號。盡管這些函數的返回類型和參數列表與普通函數類似,但它們的目的是使自定義類型能夠使用像內置類型一樣的運算符。
1.3?運算符特點:
1 定義運算符重載函數:運算符重載是通過定義特殊的成員函數或全局函數來實現的
2 運算符重載函數的返回類型:通常是運算符操作后的結果類型。
3 運算符重載函數的參數:根據運算符的類型,參數可以是一個或多個。
4??*
?::
?sizeof
??:
?.
?注意以上5個運算符不能重載
3.1 代碼解析:
運算符重載成員函數代碼示例:
//成員函數 運算符重載
class Date
{
public:int _x = 5;int _y = 4;int operator+(const Date& b){return this->_x + b._x + this->_y + b._y;}//錯誤寫法//int operator+(const Date& a , const Date& b)/*{return a.x + b.x + a.y + b.y;}*/
};
int main()
{//成員函數 運算符重載Date d1;Date d2;int sum = d1 + d2;std::cout << "d1 d2總和:" << sum << std::endl;return 0;
}
錯誤寫法分析:
因為它是Date類里面的成員函數 又因為成員函數會自帶一個隱含的this指針所以成員函數版本的 operator+
只能有一個顯式參數。如果需要兩個參數,則應使用全局函數版本的運算符重載。
//全局函數 運算符重載
class Point
{
public://默認構造函數Point(){this->_x = 5;this->_y = 15;}
//private:int _x;int _y;
};
bool operator==(const Point& b, const Point& a)
{return (b._x == a._x) && (b._y == a._y);
}int main()
{Point f1;Point f2;int B = f1 == f2;std::cout << "1相同 0相否:" << B << std::endl;return 0;
}
全局函數的運算符重載是在類外部實現的,不屬于任何類,因此沒有 this
指針。全局函數可以通過參數訪問所有操作數。
假如我把成員函數變成私有的話那在全局函數里面就找不到他們了所以想改變就只能把運算符變量改為成員函數或者用友元函數或getter方法。
二 賦值運算符重載概念:
賦值運算符重載用于定義對象之間的賦值操作,即如何將一個對象的值賦給另一個對象。賦值運算符是 =
,它在賦值時被調用。通常我們需要重載賦值運算符來處理深拷貝,以防止淺拷貝帶來的問題。
2.1 賦值運算符重載和拷貝構造的區別:
通過賦值運算符重載的概念我們知道它主要的功能是將一個對象的值賦給另一個對象,而這和拷貝構造又非常相似,然而賦值運算符重載與運算符重載只有兩字相差卻又是不同的內容,這就讓我很想知道他們之間的區別到底是什么,接下來讓我們一起來解密吧!
概念:
1. 賦值運算符重載
定義對象之間的賦值操作,即如何將一個對象的值賦給另一個對象。
2. 運算符重載:
定義或重新定義自定義類型的運算符行為,使其與內置類型的運算符行為一致。
3. 拷貝構造:
創建一個新的對象,并將其初始化為現有對象的副本。
區別:
拷貝構造函數和賦值運算符重載的主要區別在于它們的使用場景和目的。拷貝構造函數在對象創建時用于初始化新對象,目的是創建一個新的副本。賦值運算符重載在對象已存在時用于賦值操作,目的是修改現有對象的狀態,使其與另一個對象的狀態相同。拷貝構造函數通常接收一個對同類對象的常引用,而賦值運算符重載通常返回對象的引用,并接收一個對同類對象的常引用作為參數。而運算符重載和賦值運算符重載也真是差了兩個字而已,并沒有什么區別。
2.1.0 代碼解析:
//賦值運算符重載
class Date1
{
public://默認構造函數Date1(int year = 2005, int month = 5, int date = 25){this->_year = year;this->_month = month;this->_date = date;}//拷貝構造函數Date1(const Date1& other){this->_year = other._year;this->_month = other._month;this->_date= other._date;}//賦值運算符重載Date1 operator=(const Date1& d){this->_year = d._year;this->_month = d._month;this->_date = d._date;return *this;}//輸出void print(){std::cout << _year << "-" << _month << "-" << _date << std::endl;}private:int _year;int _month;int _date;
};int main()
{//構造函數Date1 q1(2024 , 7 , 12);Date1 q2(2021 , 6 , 26);//拷貝構造Date1 q3(q2);//賦值運算符重載Date1 q4;q4 = q1;//輸出q1.print();q2.print();q3.print();q4.print();return 0;
}
從上面代碼可以知道q3是在被創建的時候就直接被調用拷貝構造初始化而q4是先定義好之后在被調用賦值運算符重載初始化的。
那既然賦值運算符重載就是對象之間的賦值那和C語言中的賦值整體意思還是一樣的但就是賦值的對象變了,我不知道大家是否還記得在C語言中可以連續賦值,讓我們來試試在C++中的賦值運算符重載是否也可以實現呢?
#include <iostream>
class Date
{
public:int _year, _month, _day;Date(int year = 2005, int month = 5, int day = 25){this->_year = year;this->_month = month;this->_day = day;}// 賦值運算符重載Date& operator=(const Date& d) {// 自賦值檢查if (this != &d) { _year = d._year;_month = d._month;_day = d._day;}return *this; // 返回當前對象的引用}void Print(){std::cout << _year << "-" << _month << "-" << _day << std::endl;}
};int main() {Date d1(2023, 7, 10);Date d2;Date d3;// 鏈式賦值d3 = d2 = d1;d1.Print(); d2.Print(); d3.Print(); return 0;
}
輸出:
雖然輸出的結果確實是鏈式但是又有非常多的疑惑比如為什么要傳*this 還有執行順序是什么……
讓我們詳細的來解答一下:
首先d2傳給隱函數this指針然后d1傳給?Date& d那這時d1就是d的引用了,執行到下面就是判斷成員變量的地址是否相同(最開始它們之間的地址還是不一樣的)跳出循環之后就返回*this即d2的引用返回值類型??Date& (這個我之前一直沒看到所以就很困惑)
2.2 默認賦值運算符重載:
編譯器生成的默認賦值運算符重載也和默認構造函數中分自定義和內置類型那它與默認構造函數有什么區別,讓我們一探究竟吧
內置類型:
概念:編譯器生成的默認賦值運算符會直接逐字節拷貝內置類型成員變量的值。
代碼演示:
//默認賦值運算符重載(內置類型)
class Date1
{
public://默認構造函數Date1(int year = 2005, int month = 5, int date = 25){this->_year = year;this->_month = month;this->_date = date;}void print(){std::cout << _year << "-" << _month << "-" << _date << std::endl;}private:int _year;int _month;int _date;
};int main()
{Date1 q1(2024 , 7 , 12);Date1 q5;q5 = q1;q5.print();return 0;
}
輸出:
就如上圖所示,我并沒有寫賦值運算符重載但是它卻給我打印出和q1對象中的成員變量一樣的值,所以我們可以得出結論編譯器生成的默認賦值運算符會直接逐字節拷貝內置類型成員變量的值。俗稱淺拷貝。
自定義類型:
代碼演示:
//默認賦值運算符重載(自定義類型)
class Date2
{
public:// 賦值運算符重載Date2& operator=(const Date2& d){if (this != &d) { // 自我賦值檢查_a = d._a;}return *this;}private:int _a = 10;
};class Date3
{
public:// 默認構造函數Date3(int year = 2005, int month = 5, int date = 25){this->_year = year;this->_month = month;this->date = date;}void print(){std::cout << _year << "-" << _month << "-" << _date << std::endl;}private:int _year;int _month;int _date;Date2 c; // 包含 Date2 類型的成員變量
};int main()
{Date3 q1(2024, 7, 12);Date3 q5;q5 = q1; // 使用編譯器生成的默認賦值運算符q5.print();return 0;
}
輸出:
如圖所示編譯器生成的默認賦值運算符會調用自定義類型的賦值運算符重載。