運(yùn)算符重載的概念
- C++中的表達(dá)式由運(yùn)算符和操作數(shù)按照規(guī)則構(gòu)成。例如,算術(shù)運(yùn)算符包括加
+
、減-
、乘*
、除/
和取模%
。如果不做特殊處理,則這些算術(shù)運(yùn)算符通常只能用于對(duì)基本數(shù)據(jù)類型的常量或變量進(jìn)行運(yùn)算,而不能用于對(duì)象之間的運(yùn)算。 - 運(yùn)算符重載,就是給已有的運(yùn)算符賦予多重含義,使同一個(gè)運(yùn)算符作用于不同類型的數(shù)據(jù)時(shí)產(chǎn)生不同的行為。運(yùn)算符重載的目的是使得C++中的運(yùn)算符也能夠用來(lái)操作對(duì)象。
- 用于類運(yùn)算的運(yùn)算符通常都要重載。有兩個(gè)運(yùn)算符,系統(tǒng)提供了默認(rèn)的重載版本:賦值運(yùn)算符
=
和地址運(yùn)算符&
。
可重載的運(yùn)算符
運(yùn)算符 | 可重載的運(yùn)算符 |
---|---|
雙目運(yùn)算符 |
+ 加,- 減,* 乘,/ 除,% 取模 |
關(guān)系運(yùn)算符 |
== 等于、!= 不等于,< 小于,> 大于,<= 小于等于,>= 大于等于 |
邏輯運(yùn)算符 | 邏輯或,&& 邏輯與,! 邏輯非 |
單目運(yùn)算符 |
+ 正,- 負(fù),* 指針,& 取地址 |
自增自減運(yùn)算符 |
++ 自增,-- 自減 |
位運(yùn)算符 | 按位或,& 按位與,~ 按位取反,^ 按位異或,<< 左移,>> 右移 |
賦值運(yùn)算符 |
= 賦值,+= 加法賦值,-= 減法賦值,*= 乘法賦值,/= 除法賦值,%= 取模賦值,&= 按位與賦值,按位或賦值,^= 按位異或賦值,<<= 左移賦值,>>= 右移賦值 |
空間申請(qǐng)與釋放 |
new 創(chuàng)建對(duì)象,delete 釋放對(duì)象,new[] 創(chuàng)建數(shù)組,delete[] 釋放數(shù)組 |
其他運(yùn)算符 |
() 函數(shù)調(diào)用,-> 成員訪問(wèn),, 逗號(hào),[] 下標(biāo) |
不可重載的運(yùn)算符
運(yùn)算符 | 不可重載的運(yùn)算符 |
---|---|
成員訪問(wèn)運(yùn)算符 | . |
成員指針訪問(wèn)運(yùn)算符 |
.* ,->*
|
域運(yùn)算符 | :: |
長(zhǎng)度運(yùn)算符 | sizeof |
條件運(yùn)算符 | ?: |
預(yù)處理符號(hào) | # |
重載運(yùn)算符為類的成員函數(shù)/友元函數(shù)
//MyComplex.hpp
#ifndef MyComplex_hpp
#define MyComplex_hpp
#include <stdio.h>
class MyComplex {
private:
double real, imag;
public:
MyComplex(); //構(gòu)造函數(shù)
MyComplex(double r, double i); //構(gòu)造函數(shù)
void outCom(); //成員函數(shù)
//重載運(yùn)算符為類的成員函數(shù)
MyComplex operator-(const MyComplex &c);
//重載運(yùn)算符為友元函數(shù)
friend MyComplex operator+(const MyComplex &c1,
const MyComplex &c2);
};
#endif /* MyComplex_hpp */
//MyComplex.cpp
#include "MyComplex.hpp"
#include <iostream>
using namespace std;
MyComplex::MyComplex() {
real = 0;
imag = 0;
}
MyComplex::MyComplex(double r, double i) {
real = r;
imag = i;
}
void MyComplex::outCom() {
cout << "(" << real << ", " << imag << ")" << endl;
}
//重載運(yùn)算符為類的成員函數(shù)
MyComplex MyComplex::operator-(const MyComplex &c) {
return MyComplex(this->real - c.real,
this->imag - c.imag);
}
//重載運(yùn)算符為友元函數(shù)
MyComplex operator+(const MyComplex &c1,
const MyComplex &c2) {
return MyComplex(c1.real + c2.real,
c1.imag + c2.imag);
}
//main.cpp
#include <iostream>
#include "MyComplex.hpp"
using namespace std;
int main(int argc, const char * argv[]) {
MyComplex c1(1, 2), c2(3, 4), result;
//對(duì)象相加
c1.outCom();
cout << "oprator+" << endl;
c2.outCom();
cout << "=" << endl;
result = c1 + c2;
result.outCom();
cout << endl;
cout << "-----------" << endl;
cout << endl;
//對(duì)象相減
c1.outCom();
cout << "oprator-" << endl;
c2.outCom();
cout << "=" << endl;
result = c1 - c2;
result.outCom();
return 0;
}
/* 輸出
(1, 2)
oprator+
(3, 4)
=
(4, 6)
-----------
(1, 2)
oprator-
(3, 4)
=
(-2, -2)
*/
重載運(yùn)算符的規(guī)則
- 重載后運(yùn)算符的含義應(yīng)該符合原有的用法習(xí)慣。例如,重載
+
運(yùn)算符,完成的功能就應(yīng)該類似于做加法,在重載的+
運(yùn)算符中做減法是不合適的。 - 運(yùn)算符重載不能改變運(yùn)算符原有的語(yǔ)義,包括運(yùn)算符的優(yōu)先級(jí)和結(jié)合性。
- 運(yùn)算符重載不能改變運(yùn)算符操作數(shù)的個(gè)數(shù)及語(yǔ)法結(jié)構(gòu)。
-
重載運(yùn)算符
()
、[]
、->
或者賦值運(yùn)算符=
時(shí),只能將它們重載為成員函數(shù),不能重載為全局函數(shù)。 - 運(yùn)算符重載不能改變?cè)撨\(yùn)算符用于基本數(shù)據(jù)類型對(duì)象的含義。
重載賦值運(yùn)算符
C++中的賦值運(yùn)算符=
要求左右兩個(gè)操作數(shù)的類型是匹配的,或至少是賦值兼容的。有時(shí)希望=
兩邊的操作數(shù)的類型即使不賦值兼容也能夠成立,這就需要對(duì)=
進(jìn)行重載。C++規(guī)定,=
只能重載為成員函數(shù)。
若有類CL中定義了成員函數(shù),重載了賦值運(yùn)算符后,上述賦值語(yǔ)句將解釋為函數(shù)調(diào)用的形式:
s1.operator=(s2);
重載賦值運(yùn)算符示例
還用之前的那個(gè)MyComplex
類舉??:
//MyComplex.hpp
#include <stdio.h>
#include <string>
using namespace std;
class MyComplex {
private:
double real, imag;
public:
MyComplex(); //構(gòu)造函數(shù)
MyComplex(double r, double i); //構(gòu)造函數(shù)
void outCom(); //成員函數(shù)
void outCom(string str);
//重載運(yùn)算符為友元函數(shù)
friend MyComplex operator+(const MyComplex &c1,
const MyComplex &c2);
friend MyComplex operator+(const MyComplex &c, double r);
friend MyComplex operator+(double r, MyComplex &c);
friend MyComplex operator-(const MyComplex &c1,
const MyComplex &c2);
friend MyComplex operator-(const MyComplex &c, double r);
friend MyComplex operator-(double r, const MyComplex &c);
//賦值運(yùn)算符`=`只能重載為類的成員函數(shù)
MyComplex & operator=(const MyComplex &c);
MyComplex & operator=(double r);
};
#endif /* MyComplex_hpp */
//MyComplex.cpp
#include "MyComplex.hpp"
#include <iostream>
using namespace std;
MyComplex::MyComplex() {
real = 0;
imag = 0;
}
MyComplex::MyComplex(double r, double i) {
real = r;
imag = i;
}
void MyComplex::outCom() {
cout << "(" << real << ", " << imag << ")" << endl;
}
void MyComplex::outCom(string str) {
cout << str << " = (" << real << ", " << imag << ")" << endl;
}
MyComplex operator+(const MyComplex &c1,
const MyComplex &c2) {
return MyComplex(c1.real + c2.real,
c1.imag + c2.imag);
}
MyComplex operator+(const MyComplex &c, double r) {
return MyComplex(c.real + r, c.imag);
}
MyComplex operator+(double r, MyComplex &c) {
return MyComplex(r + c.real, c.imag);
}
MyComplex operator-(const MyComplex &c1,
const MyComplex &c2) {
return MyComplex(c1.real - c2.real, c1.imag - c2.imag);
}
MyComplex operator-(const MyComplex &c, double r) {
return MyComplex(c.real - r, c.imag);
}
MyComplex operator-(double r, const MyComplex &c) {
return MyComplex(r - c.real, c.imag);
}
MyComplex & MyComplex::operator=(const MyComplex &c) {
this->real = c.real;
this->imag = c.imag;
return *this;
}
MyComplex & MyComplex::operator=(double r) {
this->real = r;
this->imag = 0;
return *this;
}
//main.cpp
#include <iostream>
#include "MyComplex.hpp"
using namespace std;
int main(int argc, const char * argv[]) {
MyComplex c1(1, 2), c2(3, 4), result;
c1.outCom("c1");
c2.outCom("c2");
result = c1 + c2;
result.outCom("相加后賦值給 result");
result = c1 + 5;
result.outCom("c1 + 5 后賦值給 result");
result = 6 + c2;
result.outCom("6 + c2 后賦值給 result");
return 0;
}
/* 輸出:
c1 = (1, 2)
c2 = (3, 4)
相加后賦值給 result = (4, 6)
c1 + 5 后賦值給 result = (6, 2)
6 + c2 后賦值給 result = (9, 4)
*/
淺拷貝和深拷貝
同類對(duì)象之間可以通過(guò)賦值運(yùn)算符=
互相賦值。如果沒(méi)有經(jīng)過(guò)重載,=
的作用就是將賦值號(hào)右側(cè)對(duì)象的值,賦值給左側(cè)的對(duì)象。這相當(dāng)于值的拷貝,稱為淺拷貝。
重載賦值運(yùn)算符后,賦值語(yǔ)句的功能是將一個(gè)對(duì)象中指針成員變量指向的內(nèi)容復(fù)制到另一個(gè)對(duì)象中指針成員變量指向的地方,這樣的拷貝叫“深拷貝”。
#ifndef Pointer_hpp
#define Pointer_hpp
#include <stdio.h>
class Pointer {
public:
int a;
int *p;
Pointer() {
a = 100;
p = new int(10);
};
Pointer(const Pointer &tempP) {
if (this != &tempP) {
a = tempP.a;
p = tempP.p;
}
};
Pointer & operator=(const Pointer &tempP);
};
#endif /* Pointer_hpp */
//Pointer.cpp
#include "Pointer.hpp"
Pointer & Pointer::operator=(const Pointer &tempP) {
Pointer p;
p.a = tempP.a;
p.p = tempP.p;
return p;
}
#include <iostream>
#include "Pointer.hpp"
using namespace std;
int main(int argc, const char * argv[]) {
Pointer p1, p4; //構(gòu)造函數(shù)
Pointer p2(p1); //構(gòu)造函數(shù)重載
Pointer p3 = p1; //C++默認(rèn)的復(fù)制運(yùn)算符
p4 = p1; //賦值運(yùn)算符重載(內(nèi)部實(shí)現(xiàn)深拷貝)
cout << "初始化后---各對(duì)象的值及內(nèi)存地址:" << endl;
cout << "對(duì)象名\t對(duì)象地址\t\t\ta的值\tp的值\t\t p指向的值\t\tp的地址" << endl;
cout << "p1:\t\t" << &p1 << "\t" << p1.a << "\t\t" << p1.p << "\t\t" << *p1.p << "\t\t" << &p1.p << endl;
cout << "p2:\t\t" << &p2 << "\t" << p2.a << "\t\t" << p2.p << "\t\t" << *p2.p << "\t\t" << &p2.p << endl;
cout << "p3:\t\t" << &p3 << "\t" << p3.a << "\t\t" << p3.p << "\t\t" << *p3.p << "\t\t" << &p3.p << endl;
cout << "p4:\t\t" << &p4 << "\t" << p4.a << "\t\t" << p4.p << "\t\t" << *p4.p << "\t\t" << &p4.p << endl;
p4.a = 104;
p3.a = 103;
p2.a = 102;
p1.a = 101;
*p4.p = 14;/* *p4.px修改后,其他對(duì)象.p的修改不影響p4 */
*p3.p = 13;/* p1,p2,p3中p的值相同,使用的是同一個(gè)p */
*p2.p = 12;
*p1.p = 11;
cout << "修改后---各對(duì)象的值及內(nèi)存地址:" << endl;
cout << "對(duì)象名\t對(duì)象地址\t\t\ta的值\tp的值\t\t p指向的值\t\tp的地址" << endl;
cout << "p1:\t\t" << &p1 << "\t" << p1.a << "\t\t" << p1.p << "\t\t" << *p1.p << "\t\t" << &p1.p << endl;
cout << "p2:\t\t" << &p2 << "\t" << p2.a << "\t\t" << p2.p << "\t\t" << *p2.p << "\t\t" << &p2.p << endl;
cout << "p3:\t\t" << &p3 << "\t" << p3.a << "\t\t" << p3.p << "\t\t" << *p3.p << "\t\t" << &p3.p << endl;
cout << "p4:\t\t" << &p4 << "\t" << p4.a << "\t\t" << p4.p << "\t\t" << *p4.p << "\t\t" << &p4.p << endl;
return 0;
}
/*
初始化后---各對(duì)象的值及內(nèi)存地址:
對(duì)象名 對(duì)象地址 a的值 p的值 p指向的值 p的地址
p1: 0x7ffeefbff440 100 0x1006a3420 10 0x7ffeefbff448
p2: 0x7ffeefbff420 100 0x1006a3420 10 0x7ffeefbff428
p3: 0x7ffeefbff410 100 0x1006a3420 10 0x7ffeefbff418
p4: 0x7ffeefbff430 100 0x1006a2fb0 10 0x7ffeefbff438
修改后---各對(duì)象的值及內(nèi)存地址:
對(duì)象名 對(duì)象地址 a的值 p的值 p指向的值 p的地址
p1: 0x7ffeefbff440 101 0x1006a3420 11 0x7ffeefbff448
p2: 0x7ffeefbff420 102 0x1006a3420 11 0x7ffeefbff428
p3: 0x7ffeefbff410 103 0x1006a3420 11 0x7ffeefbff418
p4: 0x7ffeefbff430 104 0x1006a2fb0 14 0x7ffeefbff438
*/
重載流插入運(yùn)算符和流提取運(yùn)算符
在C++中,左移運(yùn)算符<<
可以和cout
一起用于輸出,故常被稱為“流插入運(yùn)算符”。右移運(yùn)算符>>
和cin
一起用于輸入,一般被稱為流提取運(yùn)算符。它們都是C++類庫(kù)中提供的。在類庫(kù)提供的頭文件中已經(jīng)對(duì)<<
和>>
進(jìn)行了重載,使之分別作為流插入運(yùn)算符和流提取運(yùn)算符,能用來(lái)輸出和輸入C++基本數(shù)據(jù)類型的數(shù)據(jù)。cout
是ostream
類的對(duì)象,cin
是istream
類的對(duì)象,它們都是在頭文件iostream
中聲明的。因此,凡是用cout <<
和cin >>
對(duì)基本數(shù)據(jù)類型進(jìn)行輸入/輸出的,都要用#include
指令把頭文件iostream
包含到本程序文件中。
重載強(qiáng)制類型轉(zhuǎn)換運(yùn)算符
在C++中,類型的名字(包括類的名字)本身也是一種運(yùn)算符,即強(qiáng)制類型轉(zhuǎn)換運(yùn)算符。強(qiáng)制類型轉(zhuǎn)換運(yùn)算符是單目運(yùn)算符,也可以被重載,但只能重載為成員函數(shù),不能重載為全局函數(shù)。經(jīng)過(guò)適當(dāng)重載后,(類型名)對(duì)象
這個(gè)對(duì)對(duì)象進(jìn)行強(qiáng)制類型轉(zhuǎn)換的表達(dá)式就等價(jià)于對(duì)象.operator 類型名()
,即變成對(duì)運(yùn)算符函數(shù)的調(diào)用。
重載自增、自減運(yùn)算符
自增運(yùn)算符++
和自減運(yùn)算符--
都可以被重載,但是它們有前置和后置之分。以++
為例,對(duì)于整數(shù)k,++k
和k++
的語(yǔ)義是不一樣的。
當(dāng)++
用于對(duì)象時(shí),也應(yīng)該如此。例如,obj
是一個(gè)類CDemo
的對(duì)象,那么++obj
和obj++
的含義應(yīng)該是不一樣的。按照自增運(yùn)算符及自減運(yùn)算符的本來(lái)定義,++obj
的返回值應(yīng)該是obj
被修改后的值,而obj++
的返回值應(yīng)該是obj
被修改前的值。
#ifndef CDemo_hpp
#define CDemo_hpp
#include <stdio.h>
/*
kSwitchCDemo:
0:自減運(yùn)算符重載為友元函數(shù)
1:自減運(yùn)算符重載為類的成員函數(shù)
*/
#define kSwitchCDemo (0)
class CDemo {
public:
CDemo(int i):n(i){};
operator int() {
return n;
};
CDemo & operator++();//用于前置形式
CDemo operator++(int);//用于后置形式
#if kSwitchCDemo == 1
CDemo & operator--();
CDemo operator--(int);
#else
friend CDemo & operator--(CDemo &);
friend CDemo operator--(CDemo &, int);
#endif
private:
int n;
};
#endif /* CDemo_hpp */
//CDemo.cpp
#include "CDemo.hpp"
CDemo & CDemo::operator++() {
n++;
return *this;
}
CDemo CDemo::operator++(int k) {
CDemo temp(*this);
n++;
return temp;
}
#if kSwitchCDemo == 1
//自減運(yùn)算符重載為類的成員函數(shù)
CDemo & CDemo::operator--() {
n--;
return *this;
}
CDemo CDemo::operator--(int) {
CDemo temp(*this);
n--;
return temp;
}
#else
//自減運(yùn)算符重載為友元函數(shù)
CDemo & operator--(CDemo &d) {
d.n--;
return d;
}
CDemo operator--(CDemo &d, int k) {
CDemo temp(d);
d.n--;
return temp;
}
#endif
#include <iostream>
#include "MyComplex.hpp"
#include "CDemo.hpp"
using namespace std;
int main(int argc, const char * argv[]) {
CDemo d(10);
cout << "d++ = " << d++ << endl; //d++ = 10
cout << "d = " << d << endl; //d = 11
cout << "++d = " << ++d << endl; //++d = 12
cout << "d = " << d << endl; //d = 12
cout << "d-- = " << d-- << endl; //d-- = 12
cout << "d = " << d << endl; //d = 11
cout << "--d = " << --d << endl; //--d = 10
cout << "d = " << d << endl; //d = 10
return 0;
}