🌈個人主頁:羽晨同學?
💫個人格言:“成為自己未來的主人~”??
賦值運算符重載
運算符重載
C++為了增強代碼的可讀性引入了運算符重載,運算符重載是具有特殊函數名的函數,也具有其返回值類型,函數名字以及參數列表,其返回值類型和參數列表與普通的函數類似。
?函數名字為:關鍵字operator后面接需要重載的運算符符號。
函數原型:返回值類型operator操作符(參數列表)
注意:
- 不能通過連接其他符號來創建新的操作符,比如operator@,這個其實完全沒意義。
- 重載操作符必須有一個類類型參數,這個就證明了重載操作符是針對自定義類型的,不可能針對內置類型。
- 用于內置類型的運算符,其含義不能改變,例如:內置的整型+,不能改變其含義。
- 作為類成員函數重載時,其形參看起來比操作數少一,因為有一個隱藏的this指針。
- .*? ?::? sizeof? ?:? ?.這五個運算符是不能重載的。
接下來我們先來講講這五個不能重載的運算符中的第一個。這個運算符是極其少見的。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class OB
{
public:void func(){cout << "void func()" << endl;}
};
typedef void(OB::* PtrFunc)();int main()
{PtrFunc fp = &OB::func;//成員函數取地址要加一個&,普通函數函數名就是地址OB temp;(temp.*fp)();return 0;
}
其實.*的作用是什么呢,我們可以通過這段代碼看到,.*的作用就是幫助我們調用成員函數的指針。
?接下來,我們就回到了operator關鍵字的作用,這個關鍵字可以極大的提高代碼的可讀性,我們來看下面的代碼。
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
void Test()
{Date d1(2018,9.26);Date d2(2018, 9, 27);cout << (d1 == d2) << endl;
}
這段代碼是operator的全局調用,我們會看到如果全局調用的話會存在一個問題,那就是成員變量是私有的。我們無法保證封裝性。
這里的解決辦法有三種,
- ?提供這些成員的get和set
- 友元?
- 重載為成員函數
我們一般使用的就是第三種。重載為成員函數
什么意思呢?我們來看下面的這段代碼
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}bool Func(const Date& d){return this->_year == d._year&& this->_month == d._month&& this->_day == d._day;}bool operator==(const Date& d){return this->_year == d._year&& this->_month == d._month&& this->_day == d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d3(2024, 4, 14);Date d4(2024, 4, 15);cout << d3.operator==(d4) << endl;cout << (d3 == d4) << endl;return 0;
}
?懂了嗎,我們將重載函數放到成員函數里面,就避免了權限不夠的問題。
賦值運算符重載
賦值運算符重載格式
- 參數類型:const T&,傳遞引用可以提高傳參效率。
- 返回值類型: T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續復制
- 檢測是否自己給自己復制
- 返回*this,要復合連續賦值的含義。
#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){cout << " Date(const Date& d)" << endl;_year = d._year;_month = d._month;_day = d._day;}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << "~Date()" << endl;_year = -1;_month = -1;_day = -1;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 4, 14);Date d2(d1);Date d3 = d1;return 0;
}
?2.賦值運算符只能重載成類的成員函數不能重載成全局函數
這個怎么理解呢?其實重載成類的成員函數和重載成全局函數有一個很大的不同點,就是成員函數還有一個潛在的this指針,而全局函數是沒有這個東西,所以,如果要重載為全局函數的話,我們就需要給兩個參數。
class Date
{
public:Date(int year = 1949, int month = 10, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}
我們發現這個代碼是會報錯的,?原因是由于賦值運算符如果不顯示實現,編譯器會生成一個默認的。此時用戶再在類外自己實現一個全局的賦值運算符重載,就和編譯器在類中編譯器自己生成的默認賦值運算符重載沖突了,故賦值運算符重載只能是類的成員函數。
用戶沒有顯示實現時,編譯器會生成一個默認賦值運算符重載,以值的方式逐字節拷貝,注意:內置類型成員變量是直接賦值的,而自定義類型變量是需要調用對應類的賦值運算符重載完成賦值。
class Date
{
public:Date(int year = 2024, int month = 3, int day = 4){_year = year;_month = month;_day = day;}
private:int _month;int _day;int _year;
};
int main()
{Date d1(2024, 5, 15);Date d2;d2 = d1;return 0;
}
#include<iostream>
using namespace std;
class Time
{
public:private:int _hour = 1;int _second = 1;int _minute = 1;
};
class Date
{
public:void Print(){cout << "class Time" << endl;}Date(int year = 2024, int month = 3, int day = 4){_year = year;_month = month;_day = day;}
private:int _month;int _day;int _year;Time _t;
};
int main()
{Date d1(2024, 5, 15);Date d2;d2 = d1;d1.Print();d2.Print();return 0;
}
?
通過這個結果,我們可以很明顯的得出上面的結論。
?
?
?