四、運算符重載

運算符重載的概念

  • C++中的表達式由運算符和操作數按照規則構成。例如,算術運算符包括加+、減-、乘*、除/和取模%。如果不做特殊處理,則這些算術運算符通常只能用于對基本數據類型的常量或變量進行運算,而不能用于對象之間的運算
  • 運算符重載,就是給已有的運算符賦予多重含義,使同一個運算符作用于不同類型的數據時產生不同的行為。運算符重載的目的是使得C++中的運算符也能夠用來操作對象
  • 用于類運算的運算符通常都要重載。有兩個運算符,系統提供了默認的重載版本:賦值運算符=和地址運算符&

可重載的運算符

運算符 可重載的運算符
雙目運算符 +加,-減,*乘,/除,%取模
關系運算符 ==等于、!=不等于,<小于,>大于,<=小于等于,>=大于等于
邏輯運算符 邏輯或,&&邏輯與,!邏輯非
單目運算符 +正,-負,*指針,&取地址
自增自減運算符 ++自增,--自減
位運算符 按位或,&按位與,~按位取反,^按位異或,<<左移,>>右移
賦值運算符 =賦值,+=加法賦值,-=減法賦值,*=乘法賦值,/=除法賦值,
%=取模賦值,&=按位與賦值,按位或賦值,^=按位異或賦值,
<<=左移賦值,>>=右移賦值
空間申請與釋放 new創建對象,delete釋放對象,new[]創建數組,delete[]釋放數組
其他運算符 ()函數調用,->成員訪問,,逗號,[]下標

不可重載的運算符

運算符 不可重載的運算符
成員訪問運算符 .
成員指針訪問運算符 .*->*
域運算符 ::
長度運算符 sizeof
條件運算符 ?:
預處理符號 #

重載運算符為類的成員函數/友元函數

//MyComplex.hpp
#ifndef MyComplex_hpp
#define MyComplex_hpp

#include <stdio.h>

class MyComplex {
private:
    double real, imag;
public:
    MyComplex();                            //構造函數
    MyComplex(double r, double i);          //構造函數
    void outCom();                          //成員函數
    //重載運算符為類的成員函數
    MyComplex operator-(const MyComplex &c);
    //重載運算符為友元函數
    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;
}
//重載運算符為類的成員函數
MyComplex MyComplex::operator-(const MyComplex &c) {
    return MyComplex(this->real - c.real,
                     this->imag - c.imag);
}
//重載運算符為友元函數
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)
*/

重載運算符的規則

  1. 重載后運算符的含義應該符合原有的用法習慣。例如,重載+運算符,完成的功能就應該類似于做加法,在重載的+運算符中做減法是不合適的。
  2. 運算符重載不能改變運算符原有的語義,包括運算符的優先級和結合性。
  3. 運算符重載不能改變運算符操作數的個數及語法結構
  4. 重載運算符()[]->或者賦值運算符=時,只能將它們重載為成員函數,不能重載為全局函數。
  5. 運算符重載不能改變該運算符用于基本數據類型對象的含義。

重載賦值運算符

C++中的賦值運算符=要求左右兩個操作數的類型是匹配的,或至少是賦值兼容的。有時希望=兩邊的操作數的類型即使不賦值兼容也能夠成立,這就需要對=進行重載。C++規定,=只能重載為成員函數。

若有類CL中定義了成員函數,重載了賦值運算符后,上述賦值語句將解釋為函數調用的形式:

s1.operator=(s2);

重載賦值運算符示例

還用之前的那個MyComplex類舉??:

//MyComplex.hpp
#include <stdio.h>
#include <string>

using namespace std;

class MyComplex {
private:
    double real, imag;
public:
    MyComplex();                            //構造函數
    MyComplex(double r, double i);          //構造函數
    void outCom();                          //成員函數
    void outCom(string str);
    //重載運算符為友元函數
    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);
    //賦值運算符`=`只能重載為類的成員函數
    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)
*/

淺拷貝和深拷貝

同類對象之間可以通過賦值運算符=互相賦值。如果沒有經過重載,=的作用就是將賦值號右側對象的值,賦值給左側的對象。這相當于值的拷貝,稱為淺拷貝

重載賦值運算符后,賦值語句的功能是將一個對象中指針成員變量指向的內容復制到另一個對象中指針成員變量指向的地方,這樣的拷貝叫“深拷貝”

#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;  //構造函數
    Pointer p2(p1);  //構造函數重載
    Pointer p3 = p1; //C++默認的復制運算符
    p4 = p1;         //賦值運算符重載(內部實現深拷貝)
    
    cout << "初始化后---各對象的值及內存地址:" << 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 << "修改后---各對象的值及內存地址:" << 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;
}
/*
初始化后---各對象的值及內存地址:
對象名 對象地址           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
修改后---各對象的值及內存地址:
對象名 對象地址           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++類庫中提供的。在類庫提供的頭文件中已經對<<>>進行了重載,使之分別作為流插入運算符和流提取運算符,能用來輸出和輸入C++基本數據類型的數據。coutostream類的對象,cinistream類的對象,它們都是在頭文件iostream中聲明的。因此,凡是用cout <<cin >>對基本數據類型進行輸入/輸出的,都要用#include指令把頭文件iostream包含到本程序文件中。

重載強制類型轉換運算符

在C++中,類型的名字(包括類的名字)本身也是一種運算符,即強制類型轉換運算符。強制類型轉換運算符是單目運算符,也可以被重載,但只能重載為成員函數,不能重載為全局函數。經過適當重載后,(類型名)對象這個對對象進行強制類型轉換的表達式就等價于對象.operator 類型名(),即變成對運算符函數的調用。

重載自增、自減運算符

自增運算符++和自減運算符--都可以被重載,但是它們有前置和后置之分。以++為例,對于整數k,++kk++的語義是不一樣的。

++用于對象時,也應該如此。例如,obj是一個類CDemo的對象,那么++objobj++的含義應該是不一樣的。按照自增運算符及自減運算符的本來定義,++obj的返回值應該是obj被修改后的值,而obj++的返回值應該是obj被修改前的值。

#ifndef CDemo_hpp
#define CDemo_hpp

#include <stdio.h>

/*
 kSwitchCDemo:
 0:自減運算符重載為友元函數
 1:自減運算符重載為類的成員函數
 */
#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
//自減運算符重載為類的成員函數
CDemo & CDemo::operator--() {
    n--;
    return *this;
}
CDemo CDemo::operator--(int) {
    CDemo temp(*this);
    n--;
    return temp;
}

#else
//自減運算符重載為友元函數
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;
}
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。