運算符重載1(operator overload)
運算符重載(操作符重載) : 可以爲運算符增加一些新的功能.
#include <iostream>
using namespace std;
class Point {
friend Point operator+(const Point &, const Point &);
private:
int m_x;
int m_y;
public:
Point(int x, int y) : m_x(x), m_y(y) {}
void display() {
cout << "(" << m_x << ", " << m_y << ")" << endl;
}
};
Point operator+(const Point &p1, const Point &p2) {
return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
}
int main()
{
Point p1(10, 20);
Point p2(20, 30);
Point p3 = p1 + p2;
// 本質是Point p3 = operator+(p1, p2);
p3.display();
Point p4 = p1 + p2 + p3;
// 本質是調用了兩次operator+, 第一次的結果又作爲參數傳進去
p4.display();
return 0;
}
運算符重載2-完善
(1)爲什麼拷貝構造函數的參數是const Point &point 形式?
1.必須是引用, 否則報錯.
class Point {
public:
Point(Point point) {
this->m_x = point.m_x;
this->m_y = point.m_y;
}
};
int main() {
Point p1(10, 20);
Point p2 = p1;
// 利用已經存在的對象創建新的對象,
// 調用拷貝構造函數, 相當於Point point = p1
// 又是利用已經存在的對象創建新的的對象, 又
// 要調用拷貝構造函數, 就又相當於
// Point point = p1, 從而陷入死循環.所以
// 必須是引用. 一旦變成引用就是地址值的賦值
// 就不存在創建對象了.
}
2.最好是const, 從而保證可以接受const對象和非const對象, 使接受參數的範圍更大.
運算符重載3-更多運算符
(1)一般來說跟某個對象相關的代碼, 乾脆重載爲成員函數.
一旦變爲成員函數, 就變成了
class Point {
public:
Point operator+(const Point &point) {
return Point(this->m_x + point.m_x, this->m_y + point.m_y);
// 只接受一個參數, 因爲一旦變爲成員函數, 就
// 通過對象去調用了, 比如p1 + p2 本質就變成了
// p1.operator+(p2);就意味着會把p1的地址傳給
// operator+函數, this就指向外面的p1.
// 對比原來來說簡單了, 首先只需要接受一個參數
// 其次這個功能本來就是跟Point相關的, 所以
// 直接寫在Point類裏面.而且一旦寫成成員函數就
// 意味着我們自己的私有成員變量可以訪問.
// 不需要友元函數.
}
};
(2)存在的問題:
在重載運算符的時候, 最好要保留運算符原有的特性, 比如:
因爲int a = 10, b = 20; (a + b) 這個表達式不允許被賦值(因爲a + b 返回的是常量並沒有被存儲在內存中), 所以(p1 + p2)也應該不能賦值, 因爲p1 + p2 返回的是一個臨時對象, 馬上就要銷燬了, 所以不應該可以賦值, 所以要在operator+函數的返回值前加const聲明爲返回常量對象, 不允許被賦值, 但這樣的話, 又會導致新的問題, 在p1 + p2 + p3 時, 相當於p1.operator+(p2).operator+(p3) 因爲常量對象是不能調用非常量函數的(因爲在非常量函數裏面按語法的話可以修改裏面對象的值, 而常量對象又不允許修改, 矛盾), 所以也應該把operator+函數聲明爲常量函數.最終爲
const Point operator+(const Point &point) const {
}
const Point operator-(const Point &point) const {
}
(3)+=運算符
因爲int a = 10, b = 20; (a += b) = 10; 是可以被賦值的, 因爲a += b是a + b後的結果又賦值給a, 而a可以被賦值, 所以(p1 += p2)也應該可以賦值
所以應該返回p1對象, 而返回對象(把對象作爲返回值)又會導致產生中間對象的問題, 所以應該返回引用.
class Point {
public:
Point &operator+=(const Point &point) {
this->m_x += point.m_x;
this->m_y += point.m_y;
return *this;
}
};
(4)==運算符
int a = 10, b = 20;
if (a == b) 原來是相等返回1, 不相等返回0, 所以是bool類型
bool operator==(const Point &point) const {
// 要保證常量對象可以和非常量對象比較
/* if ((m_x == point.m_x) && (m_y == point.m_y)) {
return 1;
}
else {
return 0;
} */
return (m_x == point.m_x) && (m_y == point.m_y);
}
(5)!=運算符
bool operator!=(const Point &point) const {
return (m_x != point.m_x) || (m_y != point.m_y);
}
(6)-運算符(符號)
首先-p1; 不需要傳參, p1.operator-();
而且
Point p1(10, 20);
Point p3 = -p1; 時p1並沒有改變, 所以應該返回一個臨時的
其次int a = 10, b; (-a) = 10;不允許, 因爲a並沒有被賦值, 返回的是一個臨時的值, 所以-p1不允許被賦值, 所以返回const
const Point operator-() const {
// 因爲(-(-p1))時相當於 p1.operator-().operator-(),
// 而返回的const對象不能調用非const函數, 所以函數也要聲明爲const
return Point(-m_x, -m_y);
}
(7)++, --運算符
1.爲了區分前置++和後置++規定
void operator++() {
// 是前置++
}
void operator++(int) {
// 是後置++
}
2.前置++可以被賦值, 後置++不可以被賦值
因爲int a = 10;
int b = ++a + 5;
相當於a += 1; int b = a + 5; 也就是說++a是先讓a += 1, 再把最新的a返回.所以可以被賦值.
而後置++ int a = 10;
int c = a++ + 5; 會先將a之前的值放到a++的地方等價於int c = 10 + 5; 在 a += 1; 所以a++的話並不會返回a.
3.最終實現:
前置:
Point &operator++() {
m_x++;
m_y++;
return *this;
}
後置:
而Point p2 = p1++ + Point(30, 40);
應該可以, 所以p1不能返回void而應該返回p1之前的值
所以
const Point operator++(int) {
Point old(this->m_x, this->m_y);
this->m_x++;
this->m_y++;
return old;
}
(8)<<運算符
1.因爲cout << p1 << endl;
所以<<重載函數不能是成員函數, 一旦是成員函數就必須通過Point對象調用
其實cout也是對象, 它的類是ostream
ostream -> output stream
在頭文件iostream中
2.因爲cout << 1 << 2 << endl;
所以調用完函數後應該返回cout
3.因爲(cout << 1) = cout; 返回cout後不允許被賦值, 所以const
但是返回值是const之後, 會導致cout << p1 << p2 出問題, 所以參數cout也應該const, 但是因爲系統自帶的const的定義是非const的, 所以參數是const會導致原來的const找不到.所以放棄, 但是發現其實不能被賦值, 因爲在iostream頭文件裏, 將=賦值運算符重載到了private導致外面不能訪問=
所以返回值不能是const, cout參數不能是const
ostream &operator<<(ostream &cout, const Point &point) {
cout << "(" << point.m_x << ", " << point.m_y;
return cout;
}
(9)cin運算符
input stream -> istream
1.要去掉後面參數的const, 因爲是要輸入東西去改變point對象.
istream &operator>>(istream &cin, Point &point) {
cin >> point.m_x;
cin >> point.m_y;
return cin;
}
運算符重載-調用父類的運算符重載函數
class Person {
int m_age;
public:
Person &operator=(const Person &person) {
this->m_age = person.m_age;
}
};
class Student : public Person {
int m_score;
public:
Student &operator=(const Student &student) {
Person::operator=(student);
this->m_score = student.m_score;
}
};
int main() {
Student stu;
stu.m_score = 60;
stu.m_age = 10;
Student stu1;
stu1 = stu;
}
運算符重載-仿函數(函數對象)
//int sum(int a, int b) {
// return a + b;
//}
class Sum {
int m_age;
public:
int operator()(int a, int b) {
return a + b;
}
};
int main() {
Sun sum;
cout << sum(10, 20) << endl;
// cout << sum.operator()(10, 20) << endl;
sum(20, 30);
}
仿函數和普通函數的區別是仿函數是成員函數, 意味着可以訪問類裏面的所有成員變量.
運算符重載注意點
1.有些運算符不可以被重載, 比如
(1)對象成員訪問運算符: .
(2)域運算符: ::
(3)三目運算符: ?:
(4)sizeof
2.有些運算符只能重載爲成員函數, 比如
(1)賦值運算符: =
(2)下標運算符: []
(3)函數運算符: ()
(4)指針訪問成員: ->
其他C++系列文章:
C++知識點總結(基礎語法1-函數重載, 默認參數)
C++知識點總結(基礎語法2-內聯函數, const, 引用)
C++知識點總結(面向對象1-類和對象, this指針, 內存佈局)
C++知識點總結(面向對象2-構造函數, 初始化列表)C++知識點總結(面向對象4-多繼承, 靜態成員static)
C++知識點總結(面向對象5-const成員, 拷貝構造函數)
C++知識點總結(面向對象6-隱式構造, 友元, 內部類, 局部類)
C++知識點總結(其他語法1-運算符重載)
C++知識點總結(其他語法2-模板, 類型轉換, C++11新特性)