四、運(yùn)算符重載

運(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ī)則

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

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