四、運算符重載

運算符重載的概念

  • 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ī)則

  1. 重載后運算符的含義應(yīng)該符合原有的用法習(xí)慣。例如,重載+運算符,完成的功能就應(yīng)該類似于做加法,在重載的+運算符中做減法是不合適的。
  2. 運算符重載不能改變運算符原有的語義,包括運算符的優(yōu)先級和結(jié)合性。
  3. 運算符重載不能改變運算符操作數(shù)的個數(shù)及語法結(jié)構(gòu)
  4. 重載運算符()[]->或者賦值運算符=時,只能將它們重載為成員函數(shù),不能重載為全局函數(shù)。
  5. 運算符重載不能改變該運算符用于基本數(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ù)。coutostream類的對象,cinistream類的對象,它們都是在頭文件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,++kk++的語義是不一樣的。

當(dāng)++用于對象時,也應(yīng)該如此。例如,obj是一個類CDemo的對象,那么++objobj++的含義應(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;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,702評論 2 370