1.運算符重載
C++引入運算符的目的是為了增強代碼的可讀性。運算符重載是具有特殊函數名的函數,也具有其返回值類型,函數名字以及參數列表,其返回值類型與參數列表與普通的函數相似。
函數名字為:關鍵字operator后面接需要重載的運算符符號
函數原型:返回值類型operator操作符(參數列表)
注意:
- 不能通過連接其他符號來創建新的操作符:比如operator@
- 重載操作符必須有一個類類型參數
- 用于內置類型的操作符,其含義不能改變,比如:內置的整型+,不能改變其含義
- 作為類成員函數重載時,其形參看起來比操作數數目少1,因為成員函數的第一個參數為隱藏的this
- .*? ?::? sizeof? ??:? ? .? ?注意以上五個運算符不能重載
class Date
{
public:Date(int year = 2024, int month = 12,int day = 16){_year = year;_month = month;_day = day;}
//private:int _year;int _month;int _day;
};//這里我們會發現運算符重載成全局的就需要成員變量是公有的
//我們有兩個解決方案:1.友元解決2.重載成成員函數
bool operator==(const Date& d1,const Date& d2)//這里是有問題的,在后面一個代碼會說
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}int main()
{Date d1(2024, 12, 17);Date d2(2024, 12, 18);cout << (d1 == d2) << endl;
}
class Date
{
public:Date(int year = 2024, int month = 12, int day = 16){_year = year;_month = month;_day = day;}bool operator==(const Date& d2)//看似只有一個形參,而實際有兩個形參,{ //那個看不見的形參就是this指針,return _year == d2._year //左操作數是this,指向調用函數的對象&& _month == d2._month&& _day == d2._day;}private:int _year;int _month;int _day;
};
2.賦值運算符重載
1賦值運算符重載格式
- 參數類型:const (類)& ,傳遞引用可以提高傳參的效率
- 返回值類型:(類)&,返回引用可以提供返回的效率,有返回值目的是為了支持連續賦值
- 檢測是否自己給自己賦值
- 返回*this:要復合連續賦值的含義
class Date
{
public:Date(int year = 2024, int month = 12, int day = 15){_year = year;_month=month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}Date& operator=(const Date& d){if (this != &d){_year = d._month;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2000,12,12);Date d2 = d1;return 0;
}
2賦值運算符只能重載成類的成員函數不能重載成全局函數
class Date
{
public:Date(int year = 2024, int month = 12, int day = 15){ _year = year;_month=month;_day = day;}
//private:int _year;int _month;int _day;
};//賦值運算符重載成全局函數,注意重載成全局函數時沒有this指針了,需要給兩個參數
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}
//編譯錯誤:
//"operator="必須是非靜態成員
原因:賦值運算符如果不顯現實現,編譯器會生成一個默認的。此時用戶再再類外自己實現一個全局的賦值運算符重載,就和編譯器在類中生成的默認賦值運算符重載沖突了,故賦值運算符重載只能是類的成員函數
3用戶沒有顯示實現時,編譯器會生成一個默認賦值運算符重載,以值的方式逐字節拷貝(淺拷貝)
注意:內置類型成員變量是直接賦值的,而自定義類型成員變量需要調用對應類的賦值運算符重載完成賦值。
默認生成賦值重載跟拷貝構造行為一樣:
1、內置類型成員--值拷貝/淺拷貝2、自定義類型成員會去調用他的賦值重載
class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time& operator=(const Time& t){if (this != &t){_hour = t._hour;_minute = t._minute;_second = t._second;}return *this;}private:int _hour;int _minute;int _second;
};class Date
{
private://內置類型int _year = 2024;int _month = 12;int _day = 16;//自定義類型:Time _t;
};int main()
{Date d1;Date d2;d1 = d2;return 0;
}
既然編譯器生成的默認賦值運算符重載函數已經可以完成字節序的值拷貝了,還需要自己實現嗎?那是當然的,畢竟淺拷貝無法正確處理資源管理的情況。
typedef int DataType;
class Stack
{
public:Stack(int capacity = 10){_a = (DataType*)malloc(sizeof(DataType) * capacity);if (_a == nullptr){perror("malloc::fail");return;}_capacity = capacity;_size = 0;}void Push(const DataType& data){_a[_size++] = data;}~Stack(){if (_a){free(_a);_a = nullptr;_capacity = 0;_size = 0;}}private:DataType* _a;int _size;int _capacity;
};int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 = s1;return 0;
}
注意:如果類中未涉及到資源管理,賦值運算符是否實現都可以;一旦涉及到資源管理則必須要實現。