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()
,裡面高精度該怎麼讀怎麼讀即可。