【C++ 筆記】運算子多載(Operator Overloading)範例很感謝你點進來這篇文章。
你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧!
2x2 矩陣以下範例多載了這些運算子:
運算子 型態 說明 []成員 存取矩陣元素,如 A[0][1] -(一元)成員 取負矩陣 +, -, *成員 矩陣加減乘 +=, -=, *=成員 複合指派 ==, !=成員 相等比較 *(純量)friend scalar * matrix 及 matrix * scalar<<, >>friend 標準輸出入串流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 #include <iostream> #include <stdexcept> class Matrix2x2 {private : double data[2 ][2 ]; public : Matrix2x2 () { data[0 ][0 ] = data[0 ][1 ] = data[1 ][0 ] = data[1 ][1 ] = 0.0 ; } Matrix2x2 (double a, double b, double c, double d) { data[0 ][0 ] = a; data[0 ][1 ] = b; data[1 ][0 ] = c; data[1 ][1 ] = d; } Matrix2x2 (const Matrix2x2&) = default ; Matrix2x2& operator =(const Matrix2x2&) = default ; ~Matrix2x2 () = default ; double * operator [](int row) { if (row < 0 || row > 1 ) throw std::out_of_range ("Row index out of range" ); return data[row]; } const double * operator [](int row) const { if (row < 0 || row > 1 ) throw std::out_of_range ("Row index out of range" ); return data[row]; } Matrix2x2 operator -() const { return Matrix2x2 (-data[0 ][0 ], -data[0 ][1 ], -data[1 ][0 ], -data[1 ][1 ]); } Matrix2x2 operator +(const Matrix2x2& rhs) const { return Matrix2x2 ( data[0 ][0 ] + rhs.data[0 ][0 ], data[0 ][1 ] + rhs.data[0 ][1 ], data[1 ][0 ] + rhs.data[1 ][0 ], data[1 ][1 ] + rhs.data[1 ][1 ] ); } Matrix2x2 operator -(const Matrix2x2& rhs) const { return Matrix2x2 ( data[0 ][0 ] - rhs.data[0 ][0 ], data[0 ][1 ] - rhs.data[0 ][1 ], data[1 ][0 ] - rhs.data[1 ][0 ], data[1 ][1 ] - rhs.data[1 ][1 ] ); } Matrix2x2 operator *(const Matrix2x2& rhs) const { return Matrix2x2 ( data[0 ][0 ]*rhs.data[0 ][0 ] + data[0 ][1 ]*rhs.data[1 ][0 ], data[0 ][0 ]*rhs.data[0 ][1 ] + data[0 ][1 ]*rhs.data[1 ][1 ], data[1 ][0 ]*rhs.data[0 ][0 ] + data[1 ][1 ]*rhs.data[1 ][0 ], data[1 ][0 ]*rhs.data[0 ][1 ] + data[1 ][1 ]*rhs.data[1 ][1 ] ); } Matrix2x2& operator +=(const Matrix2x2& rhs) { *this = *this + rhs; return *this ; } Matrix2x2& operator -=(const Matrix2x2& rhs) { *this = *this - rhs; return *this ; } Matrix2x2& operator *=(const Matrix2x2& rhs) { *this = *this * rhs; return *this ; } bool operator ==(const Matrix2x2& rhs) const { for (int i = 0 ; i < 2 ; ++i) for (int j = 0 ; j < 2 ; ++j) if (data[i][j] != rhs.data[i][j]) return false ; return true ; } bool operator !=(const Matrix2x2& rhs) const { return !(*this == rhs); } double det () const { return data[0 ][0 ] * data[1 ][1 ] - data[0 ][1 ] * data[1 ][0 ]; } friend Matrix2x2 operator *(double scalar, const Matrix2x2& m); friend Matrix2x2 operator *(const Matrix2x2& m, double scalar); friend std::ostream& operator <<(std::ostream& os, const Matrix2x2& m); friend std::istream& operator >>(std::istream& is, Matrix2x2& m); }; Matrix2x2 operator *(double s, const Matrix2x2& m) { return Matrix2x2 (s*m.data[0 ][0 ], s*m.data[0 ][1 ], s*m.data[1 ][0 ], s*m.data[1 ][1 ]); } Matrix2x2 operator *(const Matrix2x2& m, double s) { return s * m; } std::ostream& operator <<(std::ostream& os, const Matrix2x2& m) { os << "| " << m.data[0 ][0 ] << "\t" << m.data[0 ][1 ] << " |\n" ; os << "| " << m.data[1 ][0 ] << "\t" << m.data[1 ][1 ] << " |" ; return os; } std::istream& operator >>(std::istream& is, Matrix2x2& m) { is >> m.data[0 ][0 ] >> m.data[0 ][1 ] >> m.data[1 ][0 ] >> m.data[1 ][1 ]; return is; } int main () { Matrix2x2 A (1 , 2 , 3 , 4 ) ; Matrix2x2 B (5 , 6 , 7 , 8 ) ; std::cout << "A =\n" << A << "\n\n" ; std::cout << "B =\n" << B << "\n\n" ; std::cout << "A + B =\n" << (A + B) << "\n\n" ; std::cout << "A - B =\n" << (A - B) << "\n\n" ; std::cout << "A * B =\n" << (A * B) << "\n\n" ; std::cout << "-A =\n" << (-A) << "\n\n" ; std::cout << "3.0 * A =\n" << (3.0 * A) << "\n\n" ; std::cout << "A * 2.0 =\n" << (A * 2.0 ) << "\n\n" ; std::cout << "A == B : " << std::boolalpha << (A == B) << "\n" ; std::cout << "A != B : " << (A != B) << "\n\n" ; std::cout << "A[0][1] = " << A[0 ][1 ] << "\n\n" ; A += B; std::cout << "After A += B:\n" << A << "\n\n" ; std::cout << "det(B) = " << B.det () << "\n" ; return 0 ; }
程式碼解釋:
首先是 = default 這個語法,這個語法是 C++ 11 引入的新特性,稱為明確預設函式定義(Explicitly-defaulted function definition)。
當定義了任何建構子時,編譯器就不會再自動生成預設建構子(無參數建構子),但如果我們還需要預設建構子,可用 = default 來強制編譯器生成,不必手動撰寫空的函數體 {}。
接著第 24-34 行的程式碼:
1 2 3 4 5 6 7 8 9 10 11 double * operator [](int row) { if (row < 0 || row > 1 ) throw std::out_of_range ("Row index out of range" ); return data[row]; } const double * operator [](int row) const { if (row < 0 || row > 1 ) throw std::out_of_range ("Row index out of range" ); return data[row]; }
這邊寫的 const 只是專門給 const 用的,方便存取輸出,就這樣。
最後的 *this 指標:
1 2 3 Matrix2x2& operator +=(const Matrix2x2& rhs) { *this = *this + rhs; return *this ; } Matrix2x2& operator -=(const Matrix2x2& rhs) { *this = *this - rhs; return *this ; } Matrix2x2& operator *=(const Matrix2x2& rhs) { *this = *this * rhs; return *this ; }
this 是 C++ 每個非靜態成員函式都隱藏擁有的一個指標,指向呼叫這個函式的那個物件自己 。
例如 A += B,A 就會是呼叫 operator+= 的那個人,即 this,而 rhs 就是 B。
return *this 回傳的是參考(reference)而非複製,讓多載 += 等運算子效率更高,也支援 (A += B) += C 的連鎖(chain)寫法 。
然後這邊也複用了在先前已經事先多載好的運算子 +, -, *,直接用即可。
多項式運算以下範例多載了這些運算子:
運算子 說明 []讀寫第 i 項係數 ()代入 x 值計算 P(x),使物件像函式一樣呼叫 +, -, *多項式加減乘 +=, -=複合指派 前綴 ++ / -- 所有係數 +1 / -1 後綴 ++ / -- 同上,但回傳舊值 <<美化輸出,如 2x^2 + x - 3 ==, !=相等比較
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 #include <iostream> #include <vector> #include <cmath> #include <stdexcept> #include <algorithm> #include <initializer_list> class Polynomial {private : std::vector<double > coeffs; void trim () { while (coeffs.size () > 1 && coeffs.back () == 0.0 ) coeffs.pop_back (); } public : Polynomial () : coeffs (1 , 0.0 ) {} explicit Polynomial (double c) : coeffs(1 , c) { } Polynomial (std::initializer_list<double > list) : coeffs (list) { if (coeffs.empty ()) coeffs.push_back (0.0 ); trim (); } Polynomial (const Polynomial&) = default ; Polynomial& operator =(const Polynomial&) = default ; ~Polynomial () = default ; int degree () const { return static_cast <int >(coeffs.size ()) - 1 ; } double & operator [](int i) { if (i < 0 ) throw std::out_of_range ("Index must be >= 0" ); if (i >= static_cast <int >(coeffs.size ())) coeffs.resize (i + 1 , 0.0 ); return coeffs[i]; } double operator [](int i) const { if (i < 0 || i >= static_cast <int >(coeffs.size ())) return 0.0 ; return coeffs[i]; } double operator () (double x) const { double result = 0.0 ; for (int i = degree (); i >= 0 ; --i) result = result * x + coeffs[i]; return result; } Polynomial operator -() const { Polynomial neg (*this ) ; for (auto & c : neg.coeffs) c = -c; return neg; } Polynomial operator +(const Polynomial& rhs) const { int len = std::max (coeffs.size (), rhs.coeffs.size ()); Polynomial result; result.coeffs.resize (len, 0.0 ); for (int i = 0 ; i < len; ++i) result.coeffs[i] = (*this )[i] + rhs[i]; result.trim (); return result; } Polynomial operator -(const Polynomial& rhs) const { return *this + (-rhs); } Polynomial operator *(const Polynomial& rhs) const { int n = degree () + rhs.degree () + 1 ; Polynomial result; result.coeffs.resize (n, 0.0 ); for (int i = 0 ; i <= degree (); ++i) for (int j = 0 ; j <= rhs.degree (); ++j) result.coeffs[i + j] += coeffs[i] * rhs.coeffs[j]; result.trim (); return result; } Polynomial& operator +=(const Polynomial& rhs) { *this = *this + rhs; return *this ; } Polynomial& operator -=(const Polynomial& rhs) { *this = *this - rhs; return *this ; } Polynomial& operator *=(const Polynomial& rhs) { *this = *this * rhs; return *this ; } Polynomial& operator ++() { for (auto & c : coeffs) ++c; return *this ; } Polynomial& operator --() { for (auto & c : coeffs) --c; trim (); return *this ; } Polynomial operator ++(int ) { Polynomial old (*this ) ; ++(*this ); return old; } Polynomial operator --(int ) { Polynomial old (*this ) ; --(*this ); return old; } bool operator ==(const Polynomial& rhs) const { return coeffs == rhs.coeffs; } bool operator !=(const Polynomial& rhs) const { return !(*this == rhs); } friend std::ostream& operator <<(std::ostream& os, const Polynomial& p); }; std::ostream& operator <<(std::ostream& os, const Polynomial& p) { bool first = true ; for (int i = p.degree (); i >= 0 ; --i) { double c = p.coeffs[i]; if (c == 0.0 ) continue ; if (!first) os << (c > 0 ? " + " : " - " ); else if (c < 0 ) os << "-" ; double abs_c = std::abs (c); if (abs_c != 1.0 || i == 0 ) os << abs_c; if (i == 1 ) os << "x" ; else if (i > 1 ) os << "x^" << i; first = false ; } if (first) os << "0" ; return os; } int main () { Polynomial P = {-3 , 1 , 2 }; Polynomial Q = {5 , -1 , 0 , 1 }; std::cout << "P = " << P << "\n" ; std::cout << "Q = " << Q << "\n\n" ; std::cout << "P + Q = " << (P + Q) << "\n" ; std::cout << "P - Q = " << (P - Q) << "\n" ; std::cout << "P * Q = " << (P * Q) << "\n" ; std::cout << "-P = " << (-P) << "\n\n" ; std::cout << "P(2) = " << P (2.0 ) << "\n" ; std::cout << "Q(-1) = " << Q (-1.0 ) << "\n\n" ; P[0 ] = 10 ; std::cout << "After P[0]=10, P = " << P << "\n\n" ; Polynomial A = {1 , 2 , 3 }; std::cout << "A = " << A << "\n" ; std::cout << "++A = " << ++A << "\n" ; std::cout << "A now = " << A << "\n\n" ; Polynomial B = {0 , 1 }; std::cout << "B = " << B << "\n" ; std::cout << "B++ = " << B++ << "\n" ; std::cout << "B now = " << B << "\n\n" ; Polynomial C = {1 , 1 }; C *= C; std::cout << "(x+1)^2 = " << C << "\n\n" ; std::cout << "P == Q : " << std::boolalpha << (P == Q) << "\n" ; std::cout << "P != Q : " << (P != Q) << "\n" ; return 0 ; }
程式碼解釋:
explicit 關鍵字是用來禁止編譯器自動做隱式型別轉換(Implicit Conversion)的關鍵字,通常加在建構子前面。
假設我們沒寫,像是 Polynomial(double c) : coeffs(1, c) {},則:
1 2 3 4 5 void print (Polynomial p) { ... }print (5.0 );Polynomial p = 5.0 ;
在呼叫 print(5.0) 時,本來應該要傳 Polynomial,但傳了 double,編譯器會自動呼叫建構子把 5.0 包成 Polynomial(5.0),這種行為可能不是我們想要的,因此可透過 explicit 強制關閉這種機制。
另外 std::initializer_list 是 C++ 11 引入的型態,代表一組同型態元素的大括號初始化清單,讓我們可以在 main() 裡面用 {1, 2, 3} 這種語法初始化物件。
當編譯器看到 {-3, 1, 2} 時,會自動把它打包成一個 initializer_list<double>,然後傳給對應的建構子:
1 2 3 4 Polynomial P = {-3 , 1 , 2 };
範例程式碼中的用法:
1 2 3 4 Polynomial (std::initializer_list<double > list) : coeffs (list) { if (coeffs.empty ()) coeffs.push_back (0.0 ); trim (); }
有理分數以下範例多載了這些運算子:
運算子 說明 +, -, *, /分數四則運算 +=, -=, *=, /=複合指派 一元 +, - 正負號 ==, !=, <, >, <=, >=六大比較 operator double()隱式轉換成 double(型別轉換運算子) ++ / --(前後綴)分子 ±1(分母不變) <<, >>串流輸入輸出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 #include <iostream> #include <numeric> #include <stdexcept> #include <cmath> static long long gcd (long long a, long long b) { a = std::abs (a); b = std::abs (b); while (b) { a %= b; std::swap (a, b); } return a; } class Fraction {private : long long num; long long den; void simplify () { if (den == 0 ) throw std::invalid_argument ("Denominator cannot be zero" ); if (den < 0 ) { num = -num; den = -den; } long long g = gcd (std::abs (num), den); num /= g; den /= g; } public : Fraction (long long n = 0 , long long d = 1 ) : num (n), den (d) { simplify (); } Fraction (const Fraction&) = default ; Fraction& operator =(const Fraction&) = default ; ~Fraction () = default ; long long numerator () const { return num; } long long denominator () const { return den; } explicit operator double () const { return static_cast <double >(num) / static_cast <double >(den); } Fraction operator +() const { return *this ; } Fraction operator -() const { return Fraction (-num, den); } Fraction operator +(const Fraction& rhs) const { return Fraction (num * rhs.den + rhs.num * den, den * rhs.den); } Fraction operator -(const Fraction& rhs) const { return *this + (-rhs); } Fraction operator *(const Fraction& rhs) const { return Fraction (num * rhs.num, den * rhs.den); } Fraction operator /(const Fraction& rhs) const { if (rhs.num == 0 ) throw std::domain_error ("Division by zero fraction" ); return Fraction (num * rhs.den, den * rhs.num); } Fraction& operator +=(const Fraction& rhs) { *this = *this + rhs; return *this ; } Fraction& operator -=(const Fraction& rhs) { *this = *this - rhs; return *this ; } Fraction& operator *=(const Fraction& rhs) { *this = *this * rhs; return *this ; } Fraction& operator /=(const Fraction& rhs) { *this = *this / rhs; return *this ; } Fraction& operator ++() { num += den; simplify (); return *this ; } Fraction& operator --() { num -= den; simplify (); return *this ; } Fraction operator ++(int ) { Fraction old (*this ) ; ++(*this ); return old; } Fraction operator --(int ) { Fraction old (*this ) ; --(*this ); return old; } bool operator ==(const Fraction& rhs) const { return num == rhs.num && den == rhs.den; } bool operator !=(const Fraction& rhs) const { return !(*this == rhs); } bool operator < (const Fraction& rhs) const { return num * rhs.den < rhs.num * den; } bool operator > (const Fraction& rhs) const { return rhs < *this ; } bool operator <=(const Fraction& rhs) const { return !(rhs < *this ); } bool operator >=(const Fraction& rhs) const { return !(*this < rhs); } friend std::ostream& operator <<(std::ostream& os, const Fraction& f); friend std::istream& operator >>(std::istream& is, Fraction& f); }; std::ostream& operator <<(std::ostream& os, const Fraction& f) { if (f.den == 1 ) os << f.num; else os << f.num << "/" << f.den; return os; } std::istream& operator >>(std::istream& is, Fraction& f) { long long n, d = 1 ; char slash; is >> n; if (is.peek () == '/' ) { is >> slash >> d; } f = Fraction (n, d); return is; } int main () { Fraction a (1 , 2 ) ; Fraction b (1 , 3 ) ; Fraction c (-3 , 6 ) ; std::cout << "a = " << a << "\n" ; std::cout << "b = " << b << "\n" ; std::cout << "c = " << c << " (由 -3/6 自動化簡)\n\n" ; std::cout << "a + b = " << (a + b) << "\n" ; std::cout << "a - b = " << (a - b) << "\n" ; std::cout << "a * b = " << (a * b) << "\n" ; std::cout << "a / b = " << (a / b) << "\n\n" ; double d_a = static_cast <double >(a); std::cout << "a as double = " << d_a << "\n\n" ; std::cout << std::boolalpha; std::cout << "a == c : " << (a == c) << "\n" ; std::cout << "a > b : " << (a > b) << "\n" ; std::cout << "b < a : " << (b < a) << "\n" ; std::cout << "a >= a : " << (a >= a) << "\n\n" ; Fraction x (1 , 2 ) ; std::cout << "x = " << x << "\n" ; std::cout << "++x = " << ++x << "\n" ; std::cout << "x now = " << x << "\n\n" ; Fraction y (5 , 3 ) ; std::cout << "y = " << y << "\n" ; std::cout << "y-- = " << y-- << "\n" ; std::cout << "y now = " << y << "\n\n" ; Fraction s (1 , 4 ) ; s += Fraction (1 , 4 ); s *= Fraction (2 , 1 ); std::cout << "After += 1/4 then *= 2: s = " << s << "\n\n" ; Fraction input; std::cout << "請輸入一個分數(格式:num/den 或整數):" ; std::cin >> input; std::cout << "你輸入的是:" << input << " = " << static_cast <double >(input) << "\n" ; return 0 ; }