C++運算符重載


轉自:http://c.biancheng.net/cpp/biancheng/view/3011.html

 

一、C++運算符重載的概念和語法

所謂重載,就是賦予新的含義。函數重載(Function Overloading)可以讓一個函數名有多種功能,在不同情況下進行不同的操作。運算符重載(Operator Overloading)也是一個道理,同一個運算符可以有不同的功能。

實際上,我們已經在不知不覺中使用了運算符重載。例如,+號可以對不同類型(int、float 等)的數據進行加法操作;<<既是位移運算符,又可以配合 cout 向控制台輸出數據。C++ 本身已經對這些運算符進行了重載。

C++ 也允許程序員自己重載運算符,這給我們帶來了很大的便利。

下面的代碼定義了一個復數類,通過運算符重載,可以用+號實現復數的加法運算:

  1. #include <iostream>
  2. using namespace std;
  3. class complex{
  4. public:
  5. complex();
  6. complex(double real, double imag);
  7. public:
  8. //聲明運算符重載
  9. complex operator+(const complex &A) const;
  10. void display() const;
  11. private:
  12. double m_real; //實部
  13. double m_imag; //虛部
  14. };
  15. complex::complex(): m_real(0.0), m_imag(0.0){ }
  16. complex::complex(double real, double imag): m_real(real), m_imag(imag){ }
  17. //實現運算符重載
  18. complex complex::operator+(const complex &A) const{
  19. complex B;
  20. B.m_real = this->m_real + A.m_real;
  21. B.m_imag = this->m_imag + A.m_imag;
  22. return B;
  23. }
  24. void complex::display() const{
  25. cout<<m_real<<" + "<<m_imag<<"i"<<endl;
  26. }
  27. int main(){
  28. complex c1(4.3, 5.8);
  29. complex c2(2.4, 3.7);
  30. complex c3;
  31. c3 = c1 + c2;
  32. c3.display();
  33. return 0;
  34. }

運行結果:
6.7 + 9.5i

本例中義了一個復數類 complex,m_real 表示實部,m_imag 表示虛部,第 10 行聲明了運算符重載,第 21 行進行了實現(定義)。認真觀察這兩行代碼,可以發現運算符重載的形式與函數非常類似。

運算符重載其實就是定義一個函數,在函數體內實現想要的功能,當用到該運算符時,編譯器會自動調用這個函數。也就是說,運算符重載是通過函數實現的,它本質上是函數重載。

運算符重載的格式為:

返回值類型 operator 運算符名稱 (形參表列){
    //TODO:
}

operator是關鍵字,專門用於定義重載運算符的函數。我們可以將operator 運算符名稱這一部分看做函數名,對於上面的代碼,函數名就是operator+

運算符重載函數除了函數名有特定的格式,其它地方和普通函數並沒有區別。

上面的例子中,我們在 complex 類中重載了運算符+,該重載只對 complex 對象有效。當執行c3 = c1 + c2;語句時,編譯器檢測到+號左邊(+號具有左結合性,所以先檢測左邊)是一個 complex 對象,就會調用成員函數operator+(),也就是轉換為下面的形式:

c3 = c1.operator+(c2);

c1 是要調用函數的對象,c2 是函數的實參。

上面的運算符重載還可以有更加簡練的定義形式:

  1. complex complex::operator+(const complex &A)const{
  2. return complex(this->m_real + A.m_real, this->m_imag + A.m_imag);
  3. }

return 語句中的complex(this->m_real + A.m_real, this->m_imag + A.m_imag)會創建一個臨時對象,這個對象沒有名稱,是一個匿名對象。在創建臨時對象過程中調用構造函數,return 語句將該臨時對象作為函數返回值。

在全局范圍內重載運算符

運算符重載函數不僅可以作為類的成員函數,還可以作為全局函數。更改上面的代碼,在全局范圍內重載+,實現復數的加法運算:

  1. #include <iostream>
  2. using namespace std;
  3. class complex{
  4. public:
  5. complex();
  6. complex(double real, double imag);
  7. public:
  8. void display() const;
  9. //聲明為友元函數
  10. friend complex operator+(const complex &A, const complex &B);
  11. private:
  12. double m_real;
  13. double m_imag;
  14. };
  15. complex operator+(const complex &A, const complex &B);
  16. complex::complex(): m_real(0.0), m_imag(0.0){ }
  17. complex::complex(double real, double imag): m_real(real), m_imag(imag){ }
  18. void complex::display() const{
  19. cout<<m_real<<" + "<<m_imag<<"i"<<endl;
  20. }
  21. //在全局范圍內重載+
  22. complex operator+(const complex &A, const complex &B){
  23. complex C;
  24. C.m_real = A.m_real + B.m_real;
  25. C.m_imag = A.m_imag + B.m_imag;
  26. return C;
  27. }
  28. int main(){
  29. complex c1(4.3, 5.8);
  30. complex c2(2.4, 3.7);
  31. complex c3;
  32. c3 = c1 + c2;
  33. c3.display();
  34. return 0;
  35. }

運算符重載函數不是 complex 類的成員函數,但是卻用到了 complex 類的 private 成員變量,所以必須在 complex 類中將該函數聲明為友元函數。

當執行c3 = c1 + c2;語句時,編譯器檢測到+號兩邊都是 complex 對象,就會轉換為類似下面的函數調用:

c3 = operator+(c1, c2);

小結

雖然運算符重載所實現的功能完全可以用函數替代,但運算符重載使得程序的書寫更加人性化,易於閱讀。運算符被重載后,原有的功能仍然保留,沒有喪失或改變。通過運算符重載,擴大了C++已有運算符的功能,使之能用於對象。

 

二、C++運算符重載的規則

運算符重載是通過函數重載實現的,概念上大家都很容易理解,這節我們來說一下運算符重載的注意事項。

1) 並不是所有的運算符都可以重載。能夠重載的運算符包括:
+  -  *  /  %  ^  &  |  ~  !  =  <  >  +=  -=  *=  /=  %=  ^=  &=  |=  <<  >>  <<=  >>=  ==  !=  <=  >=  &&  ||  ++  --  ,  ->*  ->  ()  []  new  new[]  delete  delete[]

上述運算符中,[]是下標運算符,()是函數調用運算符。自增自減運算符的前置和后置形式都可以重載。長度運算符sizeof、條件運算符: ?、成員選擇符.和域解析運算符::不能被重載。

2) 重載不能改變運算符的優先級和結合性。假設上一節的 complex 類中重載了+號和*號,並且 c1、c2、c3、c4 都是 complex 類的對象,那么下面的語句:

c4 = c1 + c2 * c3;

等價於:

c4 = c1 + ( c2 * c3 );

乘法的優先級仍然高於加法,並且它們仍然是二元運算符。

3) 重載不會改變運算符的用法,原有有幾個操作數、操作數在左邊還是在右邊,這些都不會改變。例如~號右邊只有一個操作數,+號總是出現在兩個操作數之間,重載后也必須如此。

4) 運算符重載函數不能有默認的參數,否則就改變了運算符操作數的個數,這顯然是錯誤的。

5) 運算符重載函數既可以作為類的成員函數,也可以作為全局函數。

將運算符重載函數作為類的成員函數時,二元運算符的參數只有一個,一元運算符不需要參數。之所以少一個參數,是因為這個參數是隱含的。

例如,上節的 complex 類中重載了加法運算符:

complex operator+(const complex & A) const;

當執行:

c3 = c1 + c2;

會被轉換為:

c3 = c1.operator+(c2);

通過 this 指針隱式的訪問 c1 的成員變量。

將運算符重載函數作為全局函數時,二元操作符就需要兩個參數,一元操作符需要一個參數,而且其中必須有一個參數是對象,好讓編譯器區分這是程序員自定義的運算符,防止程序員修改用於內置類型的運算符的性質。

例如,下面這樣是不對的:

  1. int operator + (int a,int b){
  2. return (a-b);
  3. }

+號原來是對兩個數相加,現在企圖通過重載使它的作用改為兩個數相減, 如果允許這樣重載的話,那么表達式4+3的結果是 7 還是 1 呢?顯然,這是絕對禁止的。

如果有兩個參數,這兩個參數可以都是對象,也可以一個是對象,一個是C ++內置類型的數據,例如:

  1. complex operator+(int a, complex &c){
  2. return complex(a+c.real, c.imag);
  3. }

它的作用是使一個整數和一個復數相加。

另外,將運算符重載函數作為全局函數時,一般都需要在類中將該函數聲明為友元函數。原因很簡單,該函數大部分情況下都需要使用類的 private 成員。

上節的最后一個例子中,我們在全局范圍內重載了+號,並在 complex 類中將運算符重載函數聲明為友元函數,因為該函數使用到了 complex 類的 m_real 和 m_imag 兩個成員變量,它們都是 private 屬性的,默認不能在類的外部訪問。

6) 箭頭運算符->、下標運算符[ ]、函數調用運算符( )、賦值運算符=只能以成員函數的形式重載。

 

三、C++重載數學運算符

四則運算符(+、-、*、/、+=、-=、*=、/=)和關系運算符(>、<、<=、>=、==、!=)都是數學運算符,它們在實際開發中非常常見,被重載的幾率也很高,並且有着相似的重載格式。本節以復數類 Complex 為例對它們進行重載,重在演示運算符重載的語法以及規范。

復數能夠進行完整的四則運算,但不能進行完整的關系運算:我們只能判斷兩個復數是否相等,但不能比較它們的大小,所以不能對 >、<、<=、>= 進行重載。下面是具體的代碼:

  1. #include <iostream>
  2. #include <cmath>
  3. using namespace std;
  4. //復數類
  5. class Complex{
  6. public: //構造函數
  7. Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ }
  8. public: //運算符重載
  9. //以全局函數的形式重載
  10. friend Complex operator+(const Complex &c1, const Complex &c2);
  11. friend Complex operator-(const Complex &c1, const Complex &c2);
  12. friend Complex operator*(const Complex &c1, const Complex &c2);
  13. friend Complex operator/(const Complex &c1, const Complex &c2);
  14. friend bool operator==(const Complex &c1, const Complex &c2);
  15. friend bool operator!=(const Complex &c1, const Complex &c2);
  16. //以成員函數的形式重載
  17. Complex & operator+=(const Complex &c);
  18. Complex & operator-=(const Complex &c);
  19. Complex & operator*=(const Complex &c);
  20. Complex & operator/=(const Complex &c);
  21. public: //成員函數
  22. double real() const{ return m_real; }
  23. double imag() const{ return m_imag; }
  24. private:
  25. double m_real; //實部
  26. double m_imag; //虛部
  27. };
  28. //重載+運算符
  29. Complex operator+(const Complex &c1, const Complex &c2){
  30. Complex c;
  31. c.m_real = c1.m_real + c2.m_real;
  32. c.m_imag = c1.m_imag + c2.m_imag;
  33. return c;
  34. }
  35. //重載-運算符
  36. Complex operator-(const Complex &c1, const Complex &c2){
  37. Complex c;
  38. c.m_real = c1.m_real - c2.m_real;
  39. c.m_imag = c1.m_imag - c2.m_imag;
  40. return c;
  41. }
  42. //重載*運算符 (a+bi) * (c+di) = (ac-bd) + (bc+ad)i
  43. Complex operator*(const Complex &c1, const Complex &c2){
  44. Complex c;
  45. c.m_real = c1.m_real * c2.m_real - c1.m_imag * c2.m_imag;
  46. c.m_imag = c1.m_imag * c2.m_real + c1.m_real * c2.m_imag;
  47. return c;
  48. }
  49. //重載/運算符 (a+bi) / (c+di) = [(ac+bd) / (c²+d²)] + [(bc-ad) / (c²+d²)]i
  50. Complex operator/(const Complex &c1, const Complex &c2){
  51. Complex c;
  52. c.m_real = (c1.m_real*c2.m_real + c1.m_imag*c2.m_imag) / (pow(c2.m_real, 2) + pow(c2.m_imag, 2));
  53. c.m_imag = (c1.m_imag*c2.m_real - c1.m_real*c2.m_imag) / (pow(c2.m_real, 2) + pow(c2.m_imag, 2));
  54. return c;
  55. }
  56. //重載==運算符
  57. bool operator==(const Complex &c1, const Complex &c2){
  58. if( c1.m_real == c2.m_real && c1.m_imag == c2.m_imag ){
  59. return true;
  60. }else{
  61. return false;
  62. }
  63. }
  64. //重載!=運算符
  65. bool operator!=(const Complex &c1, const Complex &c2){
  66. if( c1.m_real != c2.m_real || c1.m_imag != c2.m_imag ){
  67. return true;
  68. }else{
  69. return false;
  70. }
  71. }
  72. //重載+=運算符
  73. Complex & Complex::operator+=(const Complex &c){
  74. this->m_real += c.m_real;
  75. this->m_imag += c.m_imag;
  76. return *this;
  77. }
  78. //重載-=運算符
  79. Complex & Complex::operator-=(const Complex &c){
  80. this->m_real -= c.m_real;
  81. this->m_imag -= c.m_imag;
  82. return *this;
  83. }
  84. //重載*=運算符
  85. Complex & Complex::operator*=(const Complex &c){
  86. this->m_real = this->m_real * c.m_real - this->m_imag * c.m_imag;
  87. this->m_imag = this->m_imag * c.m_real + this->m_real * c.m_imag;
  88. return *this;
  89. }
  90. //重載/=運算符
  91. Complex & Complex::operator/=(const Complex &c){
  92. this->m_real = (this->m_real*c.m_real + this->m_imag*c.m_imag) / (pow(c.m_real, 2) + pow(c.m_imag, 2));
  93. this->m_imag = (this->m_imag*c.m_real - this->m_real*c.m_imag) / (pow(c.m_real, 2) + pow(c.m_imag, 2));
  94. return *this;
  95. }
  96. int main(){
  97. Complex c1(25, 35);
  98. Complex c2(10, 20);
  99. Complex c3(1, 2);
  100. Complex c4(4, 9);
  101. Complex c5(34, 6);
  102. Complex c6(80, 90);
  103. Complex c7 = c1 + c2;
  104. Complex c8 = c1 - c2;
  105. Complex c9 = c1 * c2;
  106. Complex c10 = c1 / c2;
  107. cout<<"c7 = "<<c7.real()<<" + "<<c7.imag()<<"i"<<endl;
  108. cout<<"c8 = "<<c8.real()<<" + "<<c8.imag()<<"i"<<endl;
  109. cout<<"c9 = "<<c9.real()<<" + "<<c9.imag()<<"i"<<endl;
  110. cout<<"c10 = "<<c10.real()<<" + "<<c10.imag()<<"i"<<endl;
  111. c3 += c1;
  112. c4 -= c2;
  113. c5 *= c2;
  114. c6 /= c2;
  115. cout<<"c3 = "<<c3.real()<<" + "<<c3.imag()<<"i"<<endl;
  116. cout<<"c4 = "<<c4.real()<<" + "<<c4.imag()<<"i"<<endl;
  117. cout<<"c5 = "<<c5.real()<<" + "<<c5.imag()<<"i"<<endl;
  118. cout<<"c6 = "<<c6.real()<<" + "<<c6.imag()<<"i"<<endl;
  119. if(c1 == c2){
  120. cout<<"c1 == c2"<<endl;
  121. }
  122. if(c1 != c2){
  123. cout<<"c1 != c2"<<endl;
  124. }
  125. return 0;
  126. }

運行結果:
c7 = 35 + 55i
c8 = 15 + 15i
c9 = -450 + 850i
c10 = 1.9 + -0.3i
c3 = 26 + 37i
c4 = -6 + -11i
c5 = 220 + 4460i
c6 = 5.2 + 1.592i
c1 != c2

需要注意的是,我們以全局函數的形式重載了 +、-、*、/、==、!=,以成員函數的形式重載了 +=、-=、*=、/=,而且應該堅持這樣做,不能一股腦都寫作成員函數或者全局函數,具體原因我們將在下節講解。

 

四、C++重載>>和<<(輸入輸出運算符)

在C++中,標准庫本身已經對左移運算符<<和右移運算符>>分別進行了重載,使其能夠用於不同數據的輸入輸出,但是輸入輸出的對象只能是 C++ 內置的數據類型(例如 bool、int、double 等)和標准庫所包含的類類型(例如 string、complex、ofstream、ifstream 等)。

如果我們自己定義了一種新的數據類型,需要用輸入輸出運算符去處理,那么就必須對它們進行重載。本節以前面的 complex 類為例來演示輸入輸出運算符的重載。

其實 C++ 標准庫已經提供了 complex 類,能夠很好地支持復數運算,但是這里我們又自己定義了一個 complex 類,這樣做僅僅是為了教學演示。

本節要達到的目標是讓復數的輸入輸出和 int、float 等基本類型一樣簡單。假設 num1、num2 是復數,那么輸出形式就是:

cout<<num1<<num2<<endl;

輸入形式就是:

cin>>num1>>num2;

cout 是 istream 類的對象,cin 是 ostream 類的對象,要想達到這個目標,就必須以全局函數(友元函數)的形式重載<<>>,否則就要修改標准庫中的類,這顯然不是我們所期望的。

重載輸入運算符>>

下面我們以全局函數的形式重載>>,使它能夠讀入兩個 double 類型的數據,並分別賦值給復數的實部和虛部:

  1. istream & operator>>(istream &in, complex &A){
  2. in >> A.m_real >> A.m_imag;
  3. return in;
  4. }

istream 表示輸入流,cin 是 istream 類的對象,只不過這個對象是在標准庫中定義的。之所以返回 istream 類對象的引用,是為了能夠連續讀取復數,讓代碼書寫更加漂亮,例如:

complex c1, c2;
cin>>c1>>c2;

如果不返回引用,那就只能一個一個地讀取了:

complex c1, c2;
cin>>c1;
cin>>c2;

另外,運算符重載函數中用到了 complex 類的 private 成員變量,必須在 complex 類中將該函數聲明為友元函數:

friend istream & operator>>(istream & in , complex &a);

>>運算符可以按照下面的方式使用:

complex c;
cin>>c;

當輸入1.45 2.34↙后,這兩個小數就分別成為對象 c 的實部和虛部了。cin>> c;這一語句其實可以理解為:

operator<<(cin , c);

重載輸出運算符<<

同樣地,我們也可以模仿上面的形式對輸出運算符>>進行重載,讓它能夠輸出復數,請看下面的代碼:

  1. ostream & operator<<(ostream &out, complex &A){
  2. out << A.m_real <<" + "<< A.m_imag <<" i ";
  3. return out;
  4. }

ostream 表示輸出流,cout 是 ostream 類的對象。由於采用了引用的方式進行參數傳遞,並且也返回了對象的引用,所以重載后的運算符可以實現連續輸出。

為了能夠直接訪問 complex 類的 private 成員變量,同樣需要將該函數聲明為 complex 類的友元函數:

friend ostream & operator<<(ostream &out, complex &A);

綜合演示

結合輸入輸出運算符的重載,重新實現 complex 類:

  1. #include <iostream>
  2. using namespace std;
  3. class complex{
  4. public:
  5. complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ };
  6. public:
  7. friend complex operator+(const complex & A, const complex & B);
  8. friend complex operator-(const complex & A, const complex & B);
  9. friend complex operator*(const complex & A, const complex & B);
  10. friend complex operator/(const complex & A, const complex & B);
  11. friend istream & operator>>(istream & in, complex & A);
  12. friend ostream & operator<<(ostream & out, complex & A);
  13. private:
  14. double m_real; //實部
  15. double m_imag; //虛部
  16. };
  17. //重載加法運算符
  18. complex operator+(const complex & A, const complex &B){
  19. complex C;
  20. C.m_real = A.m_real + B.m_real;
  21. C.m_imag = A.m_imag + B.m_imag;
  22. return C;
  23. }
  24. //重載減法運算符
  25. complex operator-(const complex & A, const complex &B){
  26. complex C;
  27. C.m_real = A.m_real - B.m_real;
  28. C.m_imag = A.m_imag - B.m_imag;
  29. return C;
  30. }
  31. //重載乘法運算符
  32. complex operator*(const complex & A, const complex &B){
  33. complex C;
  34. C.m_real = A.m_real * B.m_real - A.m_imag * B.m_imag;
  35. C.m_imag = A.m_imag * B.m_real + A.m_real * B.m_imag;
  36. return C;
  37. }
  38. //重載除法運算符
  39. complex operator/(const complex & A, const complex & B){
  40. complex C;
  41. double square = A.m_real * A.m_real + A.m_imag * A.m_imag;
  42. C.m_real = (A.m_real * B.m_real + A.m_imag * B.m_imag)/square;
  43. C.m_imag = (A.m_imag * B.m_real - A.m_real * B.m_imag)/square;
  44. return C;
  45. }
  46. //重載輸入運算符
  47. istream & operator>>(istream & in, complex & A){
  48. in >> A.m_real >> A.m_imag;
  49. return in;
  50. }
  51. //重載輸出運算符
  52. ostream & operator<<(ostream & out, complex & A){
  53. out << A.m_real <<" + "<< A.m_imag <<" i ";;
  54. return out;
  55. }
  56. int main(){
  57. complex c1, c2, c3;
  58. cin>>c1>>c2;
  59. c3 = c1 + c2;
  60. cout<<"c1 + c2 = "<<c3<<endl;
  61. c3 = c1 - c2;
  62. cout<<"c1 - c2 = "<<c3<<endl;
  63. c3 = c1 * c2;
  64. cout<<"c1 * c2 = "<<c3<<endl;
  65. c3 = c1 / c2;
  66. cout<<"c1 / c2 = "<<c3<<endl;
  67. return 0;
  68. }

運行結果:
2.4 3.6↙
4.8 1.7↙
c1 + c2 = 7.2 + 5.3 i
c1 - c2 = -2.4 + 1.9 i
c1 * c2 = 5.4 + 21.36 i
c1 / c2 = 0.942308 + 0.705128 i

 

五、C++重載[ ](下標運算符)

C++ 規定,下標運算符[ ]必須以成員函數的形式進行重載。該重載函數在類中的聲明格式如下:

返回值類型 & operator[ ] (參數);

或者:

const 返回值類型 & operator[ ] (參數) const;

使用第一種聲明方式,[ ]不僅可以訪問元素,還可以修改元素。使用第二種聲明方式,[ ]只能訪問而不能修改元素。在實際開發中,我們應該同時提供以上兩種形式,這樣做是為了適應 const 對象,因為通過 const 對象只能調用 const 成員函數,如果不提供第二種形式,那么將無法訪問 const 對象的任何元素。

下面我們通過一個具體的例子來演示如何重載[ ]。我們知道,有些較老的編譯器不支持變長數組,例如 VC6.0、VS2010 等,這有時候會給編程帶來不便,下面我們通過自定義的 Array 類來實現變長數組。

  1. #include <iostream>
  2. using namespace std;
  3. class Array{
  4. public:
  5. Array(int length = 0);
  6. ~Array();
  7. public:
  8. int & operator[](int i);
  9. const int & operator[](int i) const;
  10. public:
  11. int length() const { return m_length; }
  12. void display() const;
  13. private:
  14. int m_length; //數組長度
  15. int *m_p; //指向數組內存的指針
  16. };
  17. Array::Array(int length): m_length(length){
  18. if(length == 0){
  19. m_p = NULL;
  20. }else{
  21. m_p = new int[length];
  22. }
  23. }
  24. Array::~Array(){
  25. delete[] m_p;
  26. }
  27. int& Array::operator[](int i){
  28. return m_p[i];
  29. }
  30. const int & Array::operator[](int i) const{
  31. return m_p[i];
  32. }
  33. void Array::display() const{
  34. for(int i = 0; i < m_length; i++){
  35. if(i == m_length - 1){
  36. cout<<m_p[i]<<endl;
  37. }else{
  38. cout<<m_p[i]<<", ";
  39. }
  40. }
  41. }
  42. int main(){
  43. int n;
  44. cin>>n;
  45. Array A(n);
  46. for(int i = 0, len = A.length(); i < len; i++){
  47. A[i] = i * 5;
  48. }
  49. A.display();
  50. const Array B(n);
  51. cout<<B[n-1]<<endl; //訪問最后一個元素
  52. return 0;
  53. }

運行結果:
5↙
0, 5, 10, 15, 20
33685536

重載[ ]運算符以后,表達式arr[i]會被轉換為:

arr.operator[ ](i);

需要說明的是,B 是 const 對象,如果 Array 類沒有提供 const 版本的operator[ ],那么第 60 行代碼將報錯。雖然第 60 行代碼只是讀取對象的數據,並沒有試圖修改對象,但是它調用了非 const 版本的operator[ ],編譯器不管實際上有沒有修改對象,只要是調用了非 const 的成員函數,編譯器就認為會修改對象(至少有這種風險)。

 

六、C++重載++和--(自增自減運算符)

自增++和自減--都是一元運算符,它的前置形式和后置形式都可以被重載。請看下面的例子:

  1. #include <iostream>
  2. #include <iomanip>
  3. using namespace std;
  4. //秒表類
  5. class stopwatch{
  6. public:
  7. stopwatch(): m_min(0), m_sec(0){ }
  8. public:
  9. void setzero(){ m_min = 0; m_sec = 0; }
  10. stopwatch run(); // 運行
  11. stopwatch operator++(); //++i,前置形式
  12. stopwatch operator++(int); //i++,后置形式
  13. friend ostream & operator<<( ostream &, const stopwatch &);
  14. private:
  15. int m_min; //分鍾
  16. int m_sec; //秒鍾
  17. };
  18. stopwatch stopwatch::run(){
  19. ++m_sec;
  20. if(m_sec == 60){
  21. m_min++;
  22. m_sec = 0;
  23. }
  24. return *this;
  25. }
  26. stopwatch stopwatch::operator++(){
  27. return run();
  28. }
  29. stopwatch stopwatch::operator++(int n){
  30. stopwatch s = *this;
  31. run();
  32. return s;
  33. }
  34. ostream &operator<<( ostream & out, const stopwatch & s){
  35. out<<setfill('0')<<setw(2)<<s.m_min<<":"<<setw(2)<<s.m_sec;
  36. return out;
  37. }
  38. int main(){
  39. stopwatch s1, s2;
  40. s1 = s2++;
  41. cout << "s1: "<< s1 <<endl;
  42. cout << "s2: "<< s2 <<endl;
  43. s1.setzero();
  44. s2.setzero();
  45. s1 = ++s2;
  46. cout << "s1: "<< s1 <<endl;
  47. cout << "s2: "<< s2 <<endl;
  48. return 0;
  49. }

運行結果:
s1: 00:00
s2: 00:01
s1: 00:01
s2: 00:01

上面的代碼定義了一個簡單的秒表類,m_min 表示分鍾,m_sec 表示秒鍾,setzero() 函數用於秒表清零,run() 函數是用來描述秒針前進一秒的動作,接下來是三個運算符重載函數。

先來看一下 run() 函數的實現,run() 函數一開始讓秒針自增,如果此時自增結果等於60了,則應該進位,分鍾加1,秒針置零。

operator++() 函數實現自增的前置形式,直接返回 run() 函數運行結果即可。

operator++ (int n) 函數實現自增的后置形式,返回值是對象本身,但是之后再次使用該對象時,對象自增了,所以在該函數的函數體中,先將對象保存,然后調用一次 run() 函數,之后再將先前保存的對象返回。在這個函數中參數n是沒有任何意義的,它的存在只是為了區分是前置形式還是后置形式。

自減運算符的重載與上面類似,這里不再贅述。

 

七、C++重載new和delete運算符

內存管理運算符 new、new[]、delete 和 delete[] 也可以進行重載,其重載形式既可以是類的成員函數,也可以是全局函數。一般情況下,內建的內存管理運算符就夠用了,只有在需要自己管理內存時才會重載。

以成員函數的形式重載 new 運算符:

void * className::operator new( size_t size ){
    //TODO:
}

以全局函數的形式重載 new 運算符:

void * operator new( size_t size ){
    //TODO:
}

兩種重載形式的返回值相同,都是void *類型,並且都有一個參數,為size_t類型。在重載 new 或 new[] 時,無論是作為成員函數還是作為全局函數,它的第一個參數必須是 size_t 類型。size_t 表示的是要分配空間的大小,對於 new[] 的重載函數而言,size_t 則表示所需要分配的所有空間的總和。

size_t 在頭文件 <cstdio> 中被定義為 typedef unsigned int size_t;,也就是無符號整型。

當然,重載函數也可以有其他參數,但都必須有默認值,並且第一個參數的類型必須是 size_t。

同樣的,delete 運算符也有兩種重載形式。以類的成員函數的形式進行重載:

void className::operator delete( void *ptr){
    //TODO:
}

以全局函數的形式進行重載:

void operator delete( void *ptr){
    //TODO:
}

兩種重載形式的返回值都是 void 類型,並且都必須有一個 void 類型的指針作為參數,該指針指向需要釋放的內存空間。

當我們以類的成員函數的形式重載了new 和 delete 操作符,其使用方法如下:

  1. C * c = new C; //分配內存空間
  2. //TODO:
  3. delete c; //釋放內存空間

如果類中沒有定義 new 和 delete 的重載函數,那么會自動調用內建的 new 和 delete 運算符。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM