<<從0到1學C++>> 第8篇 運算符重載

本篇要學習的內容和知識結構概覽


運算符重載使用場景

常規賦值操做ios

// = 賦值運算符 普通的賦值操做
int a, b;
a = 5;
b = a;複製代碼

咱們如今有一個類數組

class str {
    char * st;
    
public:
    // 使用指針的構造函數
    str(char * s) {
        st = new char[strlen(s) + 1];
        strcpy(st, s);
    }
};複製代碼

想要實現這種賦值操做bash

str s1("abc");
str s2("def");
s1 = s2;複製代碼

就須要咱們經過重載"="賦值運算符來實現了函數

具體實現以下
學習

#include <iostream>
using namespace std;

class str {
    char * st;
    
public:
    // 使用指針的構造函數
    str(char * s) {
        st = new char[strlen(s) + 1];
        strcpy(st, s);
    }
    
    // 使用引用的構造函數
    str(str & a) {
        st = new char[strlen(a.st) + 1];
        strcpy(st, a.st);
    }
    
    // 重載使用對象引用的賦值運算符
    str & operator=(str & a) {
        if (this == &a) {
            return *this;
        }
        delete st;
        st = new char(strlen(a.st) + 1);
        strcpy(st, a.st);
        return *this;
    }
    
    // 輸出字符串
    void print() {
        cout << st << endl;
    }
    
    // 析構函數
    ~str() {
        delete st;
    }
};

int main() {
    
    // 兩個字符數組
    char c1[] = "hello";
    char c2[] = "world";
    
    // 三個對象
    str s1(c1);
    str s2(c2);
    str s3(s1);
    
    // 如今咱們能夠實現表象上的 = 賦值操做
    s3 = s2 = s1;

    // 等價於下面這句話
    s3.operator=(s2.operator=(s1));

    // 等價於下面這兩句話
    s2.operator=(s1);
    s3.operator=(s2);
    
    // 打印
    s1.print();
    s2.print();
    s3.print();
}複製代碼

因此說呢: ui

咱們在使用運算符進行運算的時候, 實際上也是經過函數來實現運算的.this

任何運算都是經過函數來實現的, 因此經過運算符來進行計算, 實際也是經過函數來完成spa

運算符重載的實質

表達式 7 / 2 = 3, 7.0 / 2.0 = 3.5, 同一個運算符 / , 具備不一樣的意義, 稱之爲」運算符重載」, 實際上就是"函數重載".設計

每一個運算符都有本身的函數形式, 像下面這些指針

7 + 2 的函數形式就是 operator + (7, 2)

7 - 2 的函數形式就是 operator - (7, 2)

7 * 2 的函數形式就是 operator * (7, 2)

7 / 2 的函數形式就是 operator / (7, 2)

因此, 要重載某個運算符, 只要重載相應的函數就能夠了

好比: 

// string對象進行 + 運算
string a = "abc";
string b = "def";
cout << a + b << endl;複製代碼

定義的重載運算符都要求可以訪問這個類型的私有成員, 在這個前提下:

要麼將運算符重載爲這個類型的成員函數

要麼將運算符重載爲這個類型的友元

將做爲類的成員函數的重載運算符稱爲類運算符

將做爲類的友元重載運算符稱爲友元運算符

可重載運算符和不可重載運算符的列表

下面是: 可重載運算符與只能用類運算符重載和只能用友元運算符重載和不是運算符的區分


插入符<< 和 提取符 >> 的重載

注意: 

<< 或者 >> 輸入流和輸出流都是標準類庫, 不可修改, 因此不能在它們本身的類裏重載爲類運算符

操做符的左邊是流對象, 而不是被操做的對象,並且咱們還要訪問被操做對象的私有數據, 因此咱們只能將它們做爲被操做類對象的友元重載

#include <iostream>
using namespace std;

// 一個新的類
class Test {
    // 三個成員屬性
    int i;
    float f;
    char c;
    
public:
    
    // 構造函數
    Test(int a = 0, float b = 0, char ch = '\0') {
        i = a;
        f = b;
        c = ch;
    }
    
    // 友元重構
    friend ostream & operator << (ostream &, Test &);
    
    // 友元重構
    friend istream & operator >> (istream &, Test &);
};

// 重載插入符
ostream & operator << (ostream & output, Test & test) {
    output << test.i << ", ";
    output << test.f << ", ";
    output << test.c << endl;
    return output;
}

// 重載提取符
istream & operator >> (istream & input, Test & test) {
    input >> test.i;
    input >> test.f;
    input >> test.c;
    return input;
}

int main() {
    
    // 經過重載提取符實例一個對象
    Test a, b;
    
    // 能夠這麼寫 函數調用形式
//    operator >> (cin, a);
//    operator >> (cin, b);
    
//    operator << (cout, a);
//    operator << (cout, b);
    
    // 也能夠這麼寫 運算符形式
    cin >> a >> b;
    // << 插入符之因此能夠連續使用是由於 cout << a 這個表達式返回cout對象, 繼續輸出b對象 cout << b
//    cout << a << b << endl;
//    operator <<(operator << (cout, a), b);

}複製代碼
注意: 不能本身定義新的運算符, 只能是把原有的運算符用到本身設計的類上去

++ 運算符的重載:

做爲類運算符的重載

#include <iostream>
using namespace std;

class Number {
    int num;
    
public:
    Number(int i) {
        num = i;
    }
    
    // ++在前 編譯器默認: 重寫++時, 沒有參數, 爲++在前
    int operator ++ () {
        num++;
        return num;
    }
    
    // ++ 在後: 編譯器默認: 重寫++時, 有一個參數, 爲++在後
    int operator ++ (int) {
        int i = num;
        num ++;
        return i;
    }
    
    void print() {
        cout << num << endl;
    }
};

int main() {
    Number n(10);
//    int a = ++n;
    int a = n.operator++();
    cout << a << endl;
    n.print();
    
//    a = n++;
    a = n.operator++(0);
    cout << a << endl;
    n.print();

}複製代碼

做爲友元運算符的重載

#include <iostream>
using namespace std;

class Number {
    int num;
    
public:
    Number(int i) {
        num = i;
    }
    
    // ++ 在前: 一個參數 ++ 在前
    friend int operator ++ (Number &);
    
    // ++ 在後: 兩個參數 ++ 在後
    friend int operator ++ (Number &, int);
    
    void print() {
        cout << num << endl;
    }
};

int operator ++ (Number & a)  {
    a.num++;
    return a.num;
}

int operator ++ (Number & a, int)  {
    int i = a.num;
    a.num ++;
    return i;
}

int main() {
    Number n(10);
//    int i = ++n; 這個句子會調用operator++(Number)這個函數, 也就是++在前
    int i = operator++(n);
    cout << i << endl;
    n.print();
    
//    i = n++; 這個句子會調用operator++(Number, int)這個函數, 也就是++在後
    i = operator++(n, 0);
    cout << i << endl;
    n.print();
}複製代碼
注意:

通過重載, 運算符並不改變原有的優先級, 也不改變所需操做數目

當不涉及到定義的類對象時, 它仍然執行系統預約義的運算, 只有用到本身定義的對象止, 才執行新定義的操做

類運算符和友元運算符的區別

若是運算符所需的操做數但願進行隱式類型轉換, 則運算符應經過友元來重載

若是一個運算符的操做須要修改類對象的狀態, 則應當使用類運算符

運算符 + 做爲友元運算符

#include <iostream>
using namespace std;

class complex {
    double real, imag;
    
public:
    complex(double r = 0, double i = 0) {
        real = r;
        imag = i;
    }
    
    // 友元重載
    friend complex operator + (complex, complex);
    
    void show() {
        cout << real << "+" << imag << "i" << endl;
    }
};

// 重載運算符
complex operator + (complex a, complex b) {
    double r = a.real + b.real;
    double i = a.imag + b.imag;
    return complex(r, i);
}

int main() {
    complex x(3, 2), y;
    /**
     friend complex operator + (complex &, complex &);
     若是將友元運算符聲明成這樣: 則下面兩個語句都編譯錯誤
     */
    // 使用友元運算符咱們就能夠將 7 隱式類型轉成 7 + 0i 而後再進行 + 運算
    y = x + 7; // 等價於 y = operator(x, 7)
    y = 7 + y; // 等價於 y = operator(7, y);
    y.show();
}複製代碼

運算符 + 做爲類運算符 (會出現編譯錯誤)

#include <iostream>
using namespace std;

class complex {
    double real, imag;
    
public:
    complex(double r = 0, double i = 0) {
        real = r;
        imag = i;
    }
    
    // 類重載
    complex operator + (complex a) {
        double r = a.real + real;
        double i = a.imag + imag;
        return complex(r, i);
    }
    
    void show() {
        cout << real << "+" << imag << "i" << endl;
    }
};

int main() {
    complex x(3, 2), y;
    /**
     complex operator + (complex, complex);
     若是將類運算符聲明成這樣: y = 7 + y; 語句編譯錯誤
     */
    y = x + 7; // 等價於 y = x.operator(7)
//    y = 7 + y; // 等價於 y = 7.operator(y);
    y.show();
}複製代碼
注意:

在上面的main函數代碼中, 若是對象做爲重載運算符函數的參數, 則可使用構造函數將常量轉換成該類型的對象. 若是使用引用做爲參數, 這些常量不能做爲對象名使用, 因此編譯錯誤

總結

在學習C++這門語言的時候明顯的感受到她的一應俱全, 豐富多彩. 她有本身的不少特性, 表如今使用上就是更加的靈活, 總得來講就是: 沒有她沒有的, 只有你想不到的! 我會繼續深刻學習C++, 繼續挖掘語言的本質!

本系列文章會持續更新! 你們踊躍的留下本身的腳印吧!

👣👣👣👣👣👣👣👣

相關文章
相關標籤/搜索