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