祝各位六一快樂~
前言
1.為什么要進行運算符重載?
C++中預定義的運算符的操作對象只能是基本數據類型。但實際上,對于許多用戶自定義類型(例如類),也需要類似的運算操作。這時就必須在C++中重新定義這些運算符,賦予已有運算符新的功能,使它能夠用于特定類型執行特定的操作。
C++為了增強代碼的可讀性引入了運算符重載,運算符重載是具有特殊函數名的函數,也具有其
返回值類型,函數名字以及參數列表,其返回值類型與參數列表與普通的函數類似
2.什么是運算符重載 ?
運算符重載是通過創建運算符函數實現的,運算符函數定義了重載的運算符將要進行的操作。
1.基本知識
操作符重載,本質上就是函數重載(詳細了解可點擊閱讀函數重載),它大大豐富了已有操作符的含義,方便使用
運算符重載格式如下:
1.函數名:operator+需要重載的運算符符號
2.函數原型:返回值類型 operator+符號(形參參數列表)
3.必須有一個類類型的參數
4.? ? ?::? ? ?:? ? .? ? ? .*? ? ? ?sizeof? 這五個運算符不能重載
5.用于內置類型的運算符,其含義不能改變,例如:內置的整型+,不能改變其含義
6.作為類成員函數重載時,其形參看起來比操作數數目少1,因為成員函數的第一個參數為隱
藏的this7.不能通過連接其他符號來創建新的操作符:比如operator@
8.運算符重載時必須遵循的原則
- 重載運算符含義必須清楚;
- 重載運算符不能有二義性。
9.算符函數重載的兩種形式
- 重載為類的成員函數
- 重載為類的非成員函數 (非成員函數通常是友元函數)。
注:可以把一個運算符作為一個非成員、非友元函數重載。但是,這樣的運算符函數訪問類的私有和保護成員時,必須使用類的公有接口中提供的設置數據和讀取數據的函數,調用這些函數時會降低性能。可以內聯這些函數以提高性能。
補充知識:友元函數
一、友元函數的作用
- 提供數據共享接口:為不同類之間的成員函數,以及類的成員函數與一般函數之間提供了數據共享的接口。
- 支持類間緊密協作:當兩個或多個類之間需要進行緊密的協作和交互時,友元函數允許直接訪問私有成員,減少系統開銷,提高效率。
- 支持運算符重載:在某些情況下,可能需要重載運算符并操作兩個不同對象之間的私有數據。此時可以將相應操作符重載函數聲明為兩個類的友元。
二、友元函數的特點(重點)
- 與類的成員函數具有一樣的權限:友元函數可以訪問類的所有成員,包括私有成員。
- 不屬于任何類:友元函數是定義在類外的普通函數,不屬于任何類。
- 沒有this指針:由于友元函數不是類的成員函數,因此它沒有this指針。
三、友元函數的用法
- 聲明方式:友元函數需要在類中進行聲明,前面需要加上friend關鍵字,可以放在公有部分也可以放在私有部分。
- 多類友元:一個函數可以是多個類的友元函數,只需要在個各類中分別進行聲明。
- 調用方式:友元函數的調用與一般函數的調用方式和原理一致。
四、注意事項
- 破壞封裝性:友元函數破壞了類的封裝性和類數據的隱藏性,因此在使用時需要謹慎考慮。
- 避免過度使用:原則上應盡量少使用或不使用友元,除非確實能顯著提高開發效率。
2.經典運算符重載的代碼示例(主要以日期類為例)
2.1operator+,operator-,operator+=,operator-=
以復數類為例
代碼
#include<iostream>
using namespace std;//負數類
class complex
{
public:complex(double r = 0, double i = 0) :_real(r), _imag(i) {}complex operator +(const complex& c); //+運算符complex operator -(const complex& c);//-運算符complex& operator +=(const complex& c); //+=運算符complex& operator -=(const complex& c);//-=運算符complex& operator - ();//求負運算符void Print()const{cout << "(" << _real << "," << _imag << ")" << endl;}
private:double _real;//實部double _imag;//虛部
};
complex complex::operator +(const complex& c) //+運算符
{complex tmp;tmp._real = _real + c._real;tmp._imag = _imag + c._imag;return tmp;
}
complex complex::operator -(const complex& c) //-運算符
{complex tmp;tmp._real = _real - c._real;tmp._imag = _imag - c._imag;return tmp;
}
complex& complex::operator +=(const complex& c) //+=運算符
{_real += c._real;_imag += c._imag;return *this;
}
complex& complex::operator -=(const complex& c) //-=運算符
{_real -= c._real;_imag -= c._imag;return *this;
}
complex& complex::operator - ()//求負運算符
{_real = -_real;_imag = -_imag;return *this;
}
int main()
{complex c1(3.5, 5), c2(6, 8), c3, c4, c5;c3 = c1 + c2;c3.Print();c4 = c2 - c1;c4.Print();c1 += c2;c1.Print();c2.Print();c2 -= c1;c1.Print();c2.Print();c5 = -c2;c5.Print();return 0;
}
2.2前置operator++(--),后置operator++(--)
前置++和后置++的函數名都是operator++(沒錯,又是函數重載),他們的區別在于前置++沒有形參,后置++有一個形參int,但是我們在實際上使用時并不需要給后置++的形參int傳實參,int只是為了區分前置++和后置++的標識。
前置--和后置--也是同樣用形參int來區分。
以日期類為例
#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 0, int month = 0, int day = 0);// 拷貝構造函數// d2(d1)Date(const Date& d);// 獲取某年某月的天數int GetMonthDay(int year, int month);// 日期+=天數Date& operator+=(int day);// 日期+天數Date operator+(int day)const;// 日期-=天數Date& operator-=(int day);// 日期-天數Date operator-(int day)const;// 前置++Date& operator++();// 后置++Date operator++(int);// 后置--Date operator--(int);// 前置--Date& operator--();void print()const;// 析構函數(日期類無需清理資源,析構函數不必顯示寫)//void print();//~Date()//{//cout << "~Date()" << endl;//}
private:int _year, _month, _day;
};Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}
int Date::GetMonthDay(int year, int month)
{static int MonthDay[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)){return 29;}else{return MonthDay[month];}
}
void Date::print()const
{cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
Date& Date::operator+=(int day)
{if (day < 0){return *this -= -day;}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_month = 1;_year++;}}return *this;
}
Date Date::operator+(int day)const
{Date tmp = *this;tmp += day;return tmp;
}
Date& Date::operator-=(int day)
{if (day < 0){return *this += -day;}_day -= day;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}
Date Date::operator-(int day)const
{Date tmp = *this;tmp -= day;return tmp;
}
void Test1()
{Date d1(2024, 4, 30);Date d2 = d1 + 3;d2.print();Date d3(2024, 12, 31);Date d5 = d3;d3 += 1;d3.print();d5 = d5 - 1;d5.print();d1 -= 30;d1.print();Date d4 = d1 - 3;d4.print();
}
// 前置++
Date& Date::operator++()
{//這里直接用剛剛實現的Date& Date::operator+=(int day)//只是++相當于day=1而已//減少了代碼負擔*this += 1;return *this;
}
// 后置++
Date Date::operator++(int)
{//這里直接用剛剛實現的Date& Date::operator+(int day)Date tmp = *this;*this += 1;return tmp;
}
// 前置--
Date& Date::operator--()
{*this -= 1;return *this;
}
// 后置--
Date Date::operator--(int)
{Date tmp = *this;*this -= 1;return tmp;
}
void Test2()
{Date d1(2024, 6, 1);Date d2(2023, 12, 31);Date d3 = d1--;Date d4 = d2++;d3.print();d4.print();Date d5 = --d3;Date d6 = ++d4;d5.print();d6.print();
}
int main()
{//Test1();//可以自行測試Test2();
}
2.3operator<,operator<=,operator==,operator!=,operator>=,operator>
只需要實現operator<,operator==,其他的運算符重載就能輕松實現了,下面我們一起看一下吧
#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 0, int month = 0, int day = 0);Date(const Date& d);// 獲取某年某月的天數int GetMonthDay(int year, int month);// >運算符重載bool operator>(const Date& d);// ==運算符重載bool operator==(const Date& d);// >=運算符重載bool operator >= (const Date& d);// <運算符重載bool operator < (const Date& d);// <=運算符重載bool operator <= (const Date& d);// !=運算符重載bool operator != (const Date& d);void print()const;private:int _year, _month, _day;
};Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}
int Date::GetMonthDay(int year, int month)
{static int MonthDay[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)){return 29;}else{return MonthDay[month];}
}
void Date::print()const
{cout << _year << "年" << _month << "月" << _day << "日" << endl;
}bool Date::operator < (const Date& d)
{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){if (_day < d._day){return true;}}}return false;
}
bool Date::operator == (const Date& d)
{return _year == d._year&&_month == d._month&&_day == d._day;
}
bool Date::operator != (const Date& d)
{return !(*this == d);
}
bool Date::operator <= (const Date& d)
{return *this < d || *this == d;
}
bool Date::operator > (const Date& d)
{return !(*this <= d );
}
bool Date::operator >= (const Date& d)
{return !(*this < d);
}
int main()
{Date d1(2024, 3, 2), d2(2023, 5, 6);cout << (d1 == d2) << endl;cout << (d1 != d2) << endl;cout << (d1 <= d2) << endl;cout << (d1 >= d2) << endl;cout << (d1 < d2) << endl;cout << (d1 > d2) << endl;
}