1. 程式人生 > >C++過載運算子詳解

C++過載運算子詳解

結構體基礎

結構體,是一種可以自己編寫資料型別(如int,double等)的一種資料集合,宣告關鍵字struct,框架(聲明於main之外):

struct 名稱
{
    集合之中的變數
};//分號一定不要忘了

例如:

struct student
{
    char name[10];
    int grade,num,age;
};

這樣你就成功定義了一個“student”型別的資料集合,這意味著你可以這樣用:student Bob;
你也可以在結構體的最後加上你想定義的資料集合名稱,如:

struct student
{
    char name[10];
    int
grade,num,age; }Bob;

於是就有了一個名叫Bob的變數集合,它裡面包含有Bob的name,grade和age;
但是,可能已經有人注意到了,上文“Bob的”中的“的”怎麼使用呢?
於是有了一個運算子:.,它叫做成員運算子,也是一會會提到的少數幾個不能過載的運算子之一,它的用處在於訪問一個結構體集合中的變數(即成員變數),如:Bob.age=13;然後Bob這個集合中的年齡就被賦為了13。
當然,結構體之間是可以相互賦值的,這樣裡面的每一個成員變數都會被賦值。

結構體函式

結構體中不僅可以有成員變數,也可以有成員函式。

一般函式

這個很簡單,在成員函式中你可以使用你的成員變數,當你需要訪問自己這個結構體時,需要一個特殊的指標:*this

,對於這個最通俗的解釋是:你在一個房子(結構體)裡裝修,你需要看到或改變房子的外部,就用*this(在講過載運算子實會用到),框架:

struct 名字
{
    成員變數
    成員函式型別 成員函式名稱(引數)
    {
        函式體
    }
};

你會發現,在結構體裡寫函式和在外面寫是基本一樣的,例子:

struct Number
{
    int a,b;
    int max(){return a>b?a:b;}
    int add(){return a+b;}
    void clean(){a=b=0;}
};

可以這樣用:

Number A
; scanf("%d%d",&A.a,&A.b); printf("The bigger one:%d.\nTheir sums:%d",A.max(),A,add()); A.clean();

建構函式

建構函式是在定義結構體變數時自動呼叫的函式,用於對結構體成員初始化。
結構體原本是包含一個預設建構函式的,它沒有引數,函式體也為空,你可以修改這個函式的函式體,但引數列表必須為空。
你也可以寫其他建構函式,但引數的個數或引數型別必須不同,C++將根據實際情形選擇最合適的建構函式去呼叫。
如果你沒有增加建構函式,也沒有修改預設建構函式,預設建構函式便可以省略,但如果你自己定義了建構函式,則預設建構函式必須寫上。
也就是說你可以這樣寫:

struct num
{
    int len,a[100];
    num(){len=0;memset(a,0,sizeof(a));}
}

當你num A時,A裡面的len和a都被清零了。

過載運算子

過載運算子有什麼用呢?最常用的是高精度運算,以前我們經常用陣列來寫高精度,有了過載運算子和結構體後,就意味著你的main函式中只需要這樣寫:

Bignum A,B,C;
A.read();
B.read();
C=A+B;
C.print();
C=A*B;
C.print();

是不是很爽。
這裡就用高精度(當然不涉及壓位,要壓位的自己改改即可)來舉例子,具體的寫法這裡不會寫,大家直接百度高精度就可以了。

規則

過載是有規則的,首先,“過載運算子”是“過載”,而不是“定義”,所以你只能改變一個C++中已有的運算子,而不是定義一個本來沒有的運算子,如果你真的想這樣,請搜尋define。
1.C++只能過載C++中已有的運算子;除了少數幾個運算子不能過載外,全部可以過載,不能過載的操作符是類屬關係運算符”.”、成員指標運算子“*”(當這個作乘號時是可以過載的,你不用在意編譯器的想法~)、作用域分辨符“::”和三目運算子“?:”。
2.過載運算子後的優先順序和結合性都不會改變。
3.過載的運算子要與該運算本身的含義一致,不能導致混亂。
注意:過載運算子時,其引數個數比實際上參與運算的個數少一個。因為該物件自身也將參與運算。
如果以上規則不容易看懂,下面會有例子。

框架

具體還是要看例子

過載型別 operator/*這是一個過載運算子的關鍵字*/ 過載符號(引數)
{
    要執行的內容
    return X;//返回一個值(void則不用返回)
}

賦值過載

不用想都知道,肯定不能直接這樣寫:

int a;
Bignum A;
A=a;

所以我們要過載=使它不再只適用於相同型別變數間的賦值,而是用於將int型別賦給Bignum型別:

struct Bignum
{
    int len,a[MAXN];//這個高精數的長度和值
    Bignum(){len=0;memset(a,0,sizeof(a));}
    void/*賦值是一個不需要返回值的操作*/ operator =(int x)/*這裡的引數實際上是你等號後面的東西,等號前面只能Bignum型別,也就是說,只能寫成'Bignum = int'的格式才會執行以下內容*/
    {
        char t[MAXN];
        sprintf(t+1,"%d",x);//用於把一個變數按位存入char陣列,t+1,指標後移一位,這樣就會從1開始存
        len=strlen(t+1);//這裡的len是成員變數len
        for(int i=1;i<=len;i++)
            a[i]=t[len-i+1]-'0';//高精度倒著存
    }
}

關係運算符過載

這裡要過載的十分多:>,>=,<,<=,==…那麼是不是要一一寫呢?答案是否定的,實際上你只需要寫出一個<過載,然後其他運算子都可以用邏輯運算和已經過載的<表示出。
例如判斷>=X可以這樣寫:return !(*this<X);前面有講過*this。
想想,其他關係運算符可以怎樣表示呢?
程式碼:

bool/*不難理解,只有是或不是兩種關係*/ operator <(Bignum x)//兩個Bignum之間的比較
{
    if(len!=x.len) return len<x.len;
    for(int i=len;i>=1;i--)
        if(a[i]!=x.a[i]) 
            return a[i]<x.a[i];
    return false;//相等也是false
}
bool operator > (BIGNUM &x) {return x<*this;}//事實上反過來比較就是>了
bool operator <= (BIGNUM &x) {return !(x<*this);}
bool operator >= (BIGNUM &x) {return !(*this<x);}
bool operator == (BIGNUM &x) {return !(x<*this||*this<x);}
bool operator != (BIGNUM &x) {return x<*this||*this<x;}//這些很容易理解

同樣把上面的放在結構體中即可。

算術運算子過載

這個就要一個一個寫了,除了加減乘除模,你甚至可以寫一個開方乘方(可以隨便找一個符號過載,如|,^),這是演算法的事情,我們不說。相信經過前面的程式碼,大體實現大家已經明白,這裡再寫一個加法:

BIGNUM/*因為我們用到的結構是'Bignum = Bignum + Bignum',而且我們沒有過載其他用法的'=',所以右邊的'Bignum + Bignum'應該是返回一個Bignum,才能賦給最前面的Bignum*/ operator + (BIGNUM x)
{
    BIGNUM c;
    c.len=max(len,x.len)+1;
    for(int i=1,p=0;i<=c.len;i++)
    {
        c.a[i]=a[i]+x.a[i]+x;//a[i]為這個結構體的成員變數,x.a[i]是x的成員變數
        p=c.a[i]/10;
        c.a[i]%=10;
    }
    if(c.s[c.len-1]==0) c.len--;
    return c;//返回結果
}

輸入輸出

這個非常簡單,成員函式void print()void read(),裡面高精度該怎麼讀怎麼讀即可。