在 C++語言中,可以用關鍵字 operator 加上運算符來表示函數,叫做運算符重載。例如兩個 Box 對象相加函數:
Box Add(const Box &a, const Box &b);
可以用運算符重載來表示:
Box operator +(const Box &a, const Box &b);
運算符與普通函數在調用時的不同之處是:對於普通函數,參數出現在圓括號內;而對於運算符,參數出現在其左、右側。例如:
Box a, b, c;
…
c = Add(a, b); // 用普通函數
c = a + b; // 用運算符 +
從語法上講,運算符既可以定義為全局函數,也可以定義為成員函數。
可重載運算符/不可重載運算符
下面是可重載的運算符列表:
| 運算符類型 | 具體運算符 |
|---|---|
| 雙目算術運算符 | + (加),-(減),*(乘),/(除),% (取模) |
| 關系運算符 | ==(等於),!= (不等於),< (小於),> (大於>,<=(小於等於),>=(大於等於) |
| 邏輯運算符 | ||(邏輯或),&&(邏輯與),!(邏輯非) |
| 單目運算符 | + (正),-(負),*(指針),&(取地址) |
| 自增自減運算符 | ++(自增),--(自減) |
| 位運算符 | | (按位或),& (按位與),~(按位取反),^(按位異或),,<< (左移),>>(右移) |
| 賦值運算符 | =,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>= |
| 空間申請與釋放 | new,delete,new[ ] ,delete[] |
| 其他運算符 | ()(函數調用),->(成員訪問),,(逗號),[](下標) |
下面是不可重載的運算符列表:
.:成員訪問運算符.,->:成員指針訪問運算符:::域運算符sizeof:長度運算符?::條件運算符#: 預處理符號
一、一元運算符重載
一元運算符只對一個操作數進行操作:
- 遞增運算符(
++)和遞減運算符(--) - 一元減運算符,即負號(
-) - 邏輯非運算符(
!)
一元運算符通常出現在它們所操作的對象的左邊,比如!obj、-obj和++obj,但有時它們也可以作為后綴,比如obj++或obj--。
下面的實例演示了如何重載一元運算符。
#include <iostream>
using namespace std;
class Time
{
public:
// 自定義構造函數
Time(int t)
{
second = t;
}
// 獲得distance成員數據值
int GetDistance()
{
return second;
}
// 重載前綴遞增運算符( ++ ),前置遞增就是增加當前對象的值,並且返回當前對象
Time operator ++()
{
return Time(second++);
//distance++;
//return *this; // 也可以使用this指針
}
// 重載前綴遞減運算符( -- ) ,減少當前對象的值,並且返回當前對象
Time operator --()
{
return Time(second--);
//distance--;
//return *this; // 也可以使用this指針
}
// 重載后綴遞增運算符( ++ ),增加當前對象的值,並且返回增加值之前的該對象
Time operator ++(int)
{
Time origin = *this; // 保存原先未改變的對象
second++;
return origin;
}
// 重載后綴遞減運算符( -- ),后置遞減就是減少當前對象的值,並且返回減少值之前的該對象
Time operator --(int)
{
Time origin = *this; // 保存原先未改變的對象
second--;
return origin;
}
// 重載一元減運算符,即負號(-)
Time operator -()
{
return Time(-second);
}
private:
int second;
};
int main()
{
Time d1(5);
++d1; // 前綴遞增
d1++; // 后綴遞增
cout << "重載運算符(++):" << d1.GetDistance() << endl;
--d1; // 前綴遞減
--d1; // 后綴遞減
cout << "重載運算符(--):" << d1.GetDistance() << endl;
d1 = -d1;
cout << "重載運算符(-):" << d1.GetDistance() << endl;
return 0;
}
/*
輸出結果:
重載運算符(++):7
重載運算符(--):5
重載運算符(-):-5
*/
注意:
- 重載遞增遞減,一定要和指針的遞增遞減區分開。因為這里的重載操作的是對象,而不是指針(由於指針是內置類型,指針的遞增遞減是無法重載的),所以一般情況的遞增遞減是操作對象內部的成員變量。
- 遞增和遞減分為前置和后置情況,
a = ++b;(前置),a = b++;(后置)。因為符號一樣,所以給后置版本加一個 int 形參作為區分,這個形參是 0,但是在函數體中是用不到的,只是為了區分前置后置。
二、二元運算符重載
我們平常使用的加運算符(+)、減運算符(-)、乘運算符(*)和除運算符(/)都屬於二元運算符。
下面的實例演示了如何重載加運算符(+),減運算符(-)和乘運算符(*)。類似地,您也可以嘗試重載除運算符(/)。
#include <iostream>
using namespace std;
class Box
{
public:
Box() { }
Box(double len, double bre, double hei)
{
length = len;
breadth = bre;
height = hei;
}
double GetVolume(void)
{
return length * breadth * height;
}
// 重載 + 運算符,用於把兩個 Box 對象相加
Box operator +(const Box &another)
{
Box box;
box.length = this->length + another.length;
box.breadth = this->breadth + another.breadth;
box.height = this->height + another.height;
return box;
}
// 非成員函數的方式重載運算符 -
/* 當 2 個對象相減時是沒有順序要求的,但要重載 - 讓其與一個數字相加則有順序要求,
可以通過加一個友元函數使另一個順序的輸入合法。*/
friend Box operator -(const Box &box1, const double f);
// 重載 * 運算符,用於把兩個 Box 對象相乘
Box operator *(const Box &another)
{
Box box;
box.length = this->length * another.length;
box.breadth = this->breadth * another.breadth;
box.height = this->height * another.height;
return box;
}
private:
double length; // 長度
double breadth; // 寬度
double height; // 高度
};
// 非成員函數的方式重載運算符 -
Box operator -(const Box &box1, const double f)
{
Box box;
box.length = box1.length - f;
box.breadth = box1.breadth - f;
box.height = box1.height - f;
return box;
}
int main()
{
Box box1(1.0, 1.0, 1.0);
Box box2(2.0, 2.0, 2.0);
Box box3(0, 0, 0);
Box box4(0, 0, 0);
Box box5(0, 0, 0);
// Box1 的體積
cout << "Volume of box1 : " << box1.GetVolume() << endl;
// Box2 的體積
cout << "Volume of box2 : " << box2.GetVolume() << endl;
// 把兩個對象相加,得到 box3
box3 = box1 + box2; // 相當於box3=box1.operator+(box2);
// box3 的體積
cout << "Volume of box3 : " << box3.GetVolume() << endl;
// 把兩個對象相減,得到 box3
box4 = box1 - 3;
// box3 的體積
cout << "Volume of box4 : " << box4.GetVolume() << endl;
// 把兩個對象相乘,得到 box5
box5 = box1 * box2;
// box5 的體積
cout << "Volume of box5 : " << box5.GetVolume() << endl;
return 0;
}
/*
輸出結果:
Volume of box1 : 1
Volume of box2 : 8
Volume of box3 : 27
Volume of box4 : -8
Volume of box5 : 8
*/
三、關系運算符重載
#include <iostream>
using namespace std;
class Distance
{
public:
Distance(int t_len)
{
len = t_len;
}
// 重載 < 運算符
bool operator <(const Distance &another)
{
if (len < another.len)
return true;
else
return false;
}
private:
int len;
};
// 程序的主函數
int main()
{
Distance distant1 = 5;
Distance distant2 = 10;
if (distant1 < distant2)
cout << "distant1 < distant2" << endl;
else
cout << "distant1 >= distant2" << endl;
return 0;
}
/*
輸出結果:
distant1 < distant2
*/
四、輸入/輸出運算符重載
C++ 能夠使用流提取運算符>>和流插入運算符<<來輸入和輸出內置的數據類型。您可以重載流提取運算符和流插入運算符來操作對象等用戶自定義的數據類型。
在這里,有一點很重要,我們需要把運算符重載函數聲明為類的友元函數,這樣我們就能不用創建對象而直接調用函數。
下面的實例演示了如何重載提取運算符>>和插入運算符<<。
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student()
{
age = 0;
name = " ";
}
Student(int t_age, string t_name)
{
age = t_age;
name = t_name;
}
friend ostream &operator <<(ostream &out, const Student &stu)
{
out << "age:" << stu.age << " name:" << stu.name << endl;
return out;
}
friend istream &operator >>(istream &in, Student &stu)
{
in >> stu.age >> stu.name;
return in;
}
private:
int age;
string name;
};
// 程序的主函數
int main()
{
Student stu1(18,"xiaoMing");
Student stu2;
cin >> stu2;
cout << "stu1:" << stu1 << endl;
cout << "stu2:" << stu2 << endl;
return 0;
}
/*
輸出結果:
20
feng
stu1:age:18 name:xiaoMing
stu2:age:20 name:feng
*/
五、賦值運算符重載
就像其他運算符一樣,您可以重載賦值運算符(=),用於創建一個對象,比如拷貝構造函數。
下面的實例演示了如何重載賦值運算符。
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student()
{
age = 0;
name = " ";
}
Student(int t_age, string t_name)
{
age = t_age;
name = t_name;
}
void print()
{
cout << "stu:" << age << " " << name << endl;
}
void operator =(const Student &stu)
{
age = stu.age;
name = stu.name;
}
private:
int age;
string name;
};
// 程序的主函數
int main()
{
Student stu1(18,"xiaoMing");
Student stu2;
stu2 = stu1;
stu2.print();
return 0;
}
/*
輸出結果:
stu:18 xiaoMing
*/
六、函數調用運算符 () 重載
函數調用運算符()可以被重載用於類的對象。當重載()時,您不是創造了一種新的調用函數的方式,相反地,這是創建一個可以傳遞任意數目參數的運算符函數。
下面的實例演示了如何重載函數調用運算符()。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到無窮
int inches; // 0 到 12
public:
// 所需的構造函數
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 重載函數調用運算符
Distance operator()(int a, int b, int c)
{
Distance D;
// 進行隨機計算
D.feet = a + c + 10;
D.inches = b + c + 100 ;
return D;
}
// 顯示距離的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches << endl;
}
};
int main()
{
Distance D1(11, 10), D2;
cout << "First Distance : ";
D1.displayDistance();
D2 = D1(10, 10, 10); // invoke operator()
cout << "Second Distance :";
D2.displayDistance();
return 0;
}
七、下標運算符 [] 重載
下標操作符[]通常用於訪問數組元素。重載該運算符用於增強操作 C++ 數組的功能。
下面的實例演示了如何重載下標運算符[]。
#include <iostream>
using namespace std;
const int SIZE = 10;
class safearay
{
private:
int arr[SIZE];
public:
safearay()
{
register int i;
for (i = 0; i < SIZE; i++)
{
arr[i] = i;
}
}
int& operator[](int i)
{
if (i > SIZE)
{
cout << "索引超過最大值" << endl;
// 返回第一個元素
return arr[0];
}
return arr[i];
}
};
int main()
{
safearay A;
cout << "A[2] 的值為 : " << A[2] << endl;
cout << "A[5] 的值為 : " << A[5] << endl;
cout << "A[12] 的值為 : " << A[12] << endl;
return 0;
}
/*
輸出結果:
A[2] 的值為 : 2
A[5] 的值為 : 5
索引超過最大值
A[12] 的值為 : 0
*/
八、類成員訪問運算符 -> 重載
類成員訪問運算符(->)可以被重載,但它較為麻煩。它被定義用於為一個類賦予 "指針" 行為。運算符->必須是一個成員函數。如果使用了->運算符,返回類型必須是指針或者是類的對象。
運算符->通常與指針引用運算符*結合使用,用於實現"智能指針"的功能。這些指針是行為與正常指針相似的對象,唯一不同的是,當您通過指針訪問對象時,它們會執行其他的任務。比如,當指針銷毀時,或者當指針指向另一個對象時,會自動刪除對象。
間接引用運算符->可被定義為一個一元后綴運算符。也就是說,給出一個類:
class Ptr{
//...
X * operator->();
};
類 Ptr 的對象可用於訪問類 X 的成員,使用方式與指針的用法十分相似。例如:
void f(Ptr p )
{
p->m = 10 ; // (p.operator->())->m = 10
}
語句p->m被解釋為(p.operator->())->m。同樣地,下面的實例演示了如何重載類成員訪問運算符->。
#include <iostream>
#include <vector>
using namespace std;
// 假設一個實際的類
class Obj
{
static int i, j;
public:
void f() const { cout << i++ << endl; }
void g() const { cout << j++ << endl; }
};
// 靜態成員定義
int Obj::i = 10;
int Obj::j = 12;
// 為上面的類實現一個容器
class ObjContainer
{
public:
void add(Obj* obj)
{
a.push_back(obj); // 調用向量的標准方法
}
friend class SmartPointer;
private:
vector<Obj*> a;
};
// 實現智能指針,用於訪問類 Obj 的成員
class SmartPointer
{
public:
SmartPointer(ObjContainer& objc)
{
oc = objc;
index = 0;
}
// 返回值表示列表結束
bool operator++() // 前綴版本
{
if (index >= oc.a.size() - 1) return false;
if (oc.a[++index] == 0) return false;
return true;
}
bool operator++(int) // 后綴版本
{
return operator++();
}
// 重載運算符 ->
Obj* operator->() const
{
if (!oc.a[index])
{
cout << "Zero value";
return (Obj*)0;
}
return oc.a[index];
}
private:
ObjContainer oc;
int index;
};
int main()
{
const int sz = 3;
Obj o[sz];
ObjContainer oc;
for (int i = 0; i < sz; i++)
{
oc.add(&o[i]);
}
SmartPointer sp(oc); // 創建一個迭代器
do
{
sp->f(); // 智能指針調用
sp->g();
}
while (sp++);
return 0;
}
/*
輸出結果:
10
12
11
13
12
14
*/
注意:
- 運算重載符不可以改變語法結構;
- 運算重載符不可以改變操作數的個數;
- 運算重載符不可以改變優先級;
- 運算重載符不可以改變結合性。
