C++ 中,運算符重載是一種特殊的函數,它允許程序員為自定義的數據類型(如類和結構體)重新定義運算符的行為,使得這些運算符能夠像處理內置數據類型一樣處理自定義類型的數據。下面將從多個方面詳細講解 C++ 里的運算符重載。
基本語法?
運算符重載的本質是定義一個函數,其一般形式如下:
返回類型 operator運算符(參數列表) {// 函數體
}
其中,operator
?是關鍵字,后面緊跟要重載的運算符,參數列表根據運算符的不同而有所變化。
運算符重載的類型
1. 成員函數重載
成員函數重載的運算符函數定義在類的內部,它隱含了一個?this
?指針,指向調用該運算符的對象。
示例:
#include <iostream>class Complex {
private:double real;double imag;
public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 重載 + 運算符Complex operator+(const Complex& other) const {return Complex(real + other.real, imag + other.imag);}void display() const {std::cout << real << " + " << imag << "i" << std::endl;}
};int main() {Complex c1(1, 2);Complex c2(3, 4);Complex c3 = c1 + c2;c3.display();return 0;
}
在上述代碼中,operator+
?是一個成員函數,它接受一個?const Complex&
?類型的參數,返回一個新的?Complex
?對象,表示兩個復數相加的結果。
2. 非成員函數重載
非成員函數重載的運算符函數定義在類的外部,通常用于需要訪問類的私有成員的情況,此時需要將該函數聲明為類的友元函數。
示例:
#include <iostream>class Complex {
private:double real;double imag;
public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 聲明友元函數friend Complex operator+(const Complex& c1, const Complex& c2);void display() const {std::cout << real << " + " << imag << "i" << std::endl;}
};// 非成員函數重載 + 運算符
Complex operator+(const Complex& c1, const Complex& c2) {return Complex(c1.real + c2.real, c1.imag + c2.imag);
}int main() {Complex c1(1, 2);Complex c2(3, 4);Complex c3 = c1 + c2;c3.display();return 0;
}
在這個例子中,operator+
?是一個非成員函數,它接受兩個?const Complex&
?類型的參數,返回一個新的?Complex
?對象。通過將其聲明為?Complex
?類的友元函數,它可以訪問?Complex
?類的私有成員。
可重載和不可重載的運算符
- 可重載的運算符:大多數運算符都可以重載,包括算術運算符(
+
、-
、*
、/
?等)、關系運算符(==
、!=
、<
、>
?等)、邏輯運算符(&&
、||
、!
)、位運算符(&
、|
、^
?等)、賦值運算符(=
、+=
、-=
?等)、自增自減運算符(++
、--
)等。 - 不可重載的運算符:一些運算符是不能重載的,如?
.
(成員訪問運算符)、.*
(成員指針訪問運算符)、::
(作用域解析運算符)、?:
(條件運算符)和?sizeof
?運算符。
注意事項
- 不能改變運算符的優先級和結合性:運算符重載只是改變了運算符對自定義類型的操作行為,不能改變運算符原有的優先級和結合性。
- 不能創建新的運算符:只能對已有的運算符進行重載,不能創造新的運算符。
- 保持運算符的原有語義:在重載運算符時,應盡量保持運算符原有的語義,避免造成混淆。例如,重載?
+
?運算符應該實現加法的功能。
以下再舉幾個例子來解釋說明運算符重載相關注意事項
重載操作符至少有一個類類型參數,不能改變內置類型對象含義。
“不能寫?
int operator+(int x, int y)
?這種試圖改變內置?int
?類型加法含義的重載函數”??
1. C++ 的設計原則
C++ 語言設計時,為了保證語言的穩定性和可預測性,規定內置數據類型的運算符行為是固定的,不能被修改。例如,對于兩個?int
?類型的變量?a
?和?b
,a + b
?就是執行標準的整數加法運算,這是所有 C++ 程序員都默認和熟悉的行為。如果允許重載改變其含義,那么代碼的可讀性和一致性就會被破壞,可能會導致很多難以調試和理解的問題。
2. 示例說明
假設允許寫?int operator+(int x, int y)
?這樣的函數來重載?int
?類型的?+
?運算符,比如:
int operator+(int x, int y) {return x * y; // 這里試圖讓 + 運算符執行乘法的功能
}
然后在代碼中使用:
int main() {int num1 = 3;int num2 = 4;int result = num1 + num2; // 按照正常理解應該是 3 + 4 = 7,但因為重載改變了行為,實際執行的是 3 * 4 = 12return 0;
}
這樣就會讓原本熟悉?int
?類型加法的程序員產生困惑,并且可能在各種依賴于標準加法運算的代碼中引入錯誤,因為大家都默認?int
?類型的?+
?就是執行加法。
3. 正確的重載應用場景
運算符重載的正確使用場景是針對自定義類型。比如定義一個?Fraction
?類表示分數:
class Fraction {
private:int numerator; // 分子int denominator; // 分母
public:Fraction(int num, int den) : numerator(num), denominator(den) {}// 重載 + 運算符,用于分數的加法Fraction operator+(const Fraction& other) {int newNumerator = numerator * other.denominator + other.numerator * denominator;int newDenominator = denominator * other.denominator;return Fraction(newNumerator, newDenominator);}
};
?在這個例子中,重載?+
?運算符使得?Fraction
?類型的對象可以像內置類型一樣進行加法操作,而不會影響?int
?等內置類型的?+
?運算符的正常行為。
重載函數參數個數
一元運算符有一個參數,二元運算符有兩個參數。成員函數形式的運算符重載,第一個運算對象由?
this
?指針隱式傳遞,參數比運算對象少一個。
class Counter {
private:int count;
public:Counter(int c = 0) : count(c) {}// 成員函數形式重載 ++ 一元運算符Counter operator++() {count++;return *this;}
};
?上述代碼中,Counter
?類以成員函數重載前置?++
?運算符,它只有一個隱含的?this
?指針,無需額外參數。
以重載前置自增運算符?++
?為例,假設有一個?Counter
?類表示計數器:
class Counter {
private:int value;
public:Counter(int v = 0) : value(v) {}// 重載前置自增運算符,一元運算符,有一個參數(隱含的this指針指向調用對象)Counter operator++() {value++;return *this;}void display() {std::cout << "Value: " << value << std::endl;}
};
在上述代碼中,operator++
?是重載的前置自增運算符函數。當使用?++counter
?(counter
?是?Counter
?類的對象)這樣的表達式時,operator++
?函數被調用,this
?指針指向?counter
?對象,函數對?value
?進行自增操作。從參數角度看,這個函數雖然沒有顯式的參數,但通過?this
?指針隱式地接收了一個運算對象,也就是調用該運算符的對象本身。
二元運算符
二元運算符作用于兩個運算對象。在運算符重載函數中,就需要有兩個參數來分別接收這兩個運算對象。比如重載?+
?運算符用于兩個自定義類對象相加。
假設有一個?Complex
?類表示復數:
class Complex {
private:double real;double imag;
public:Complex(double r = 0, double i = 0) : real(r), imag(i) {}// 非成員函數重載 + 運算符,二元運算符,有兩個參數friend Complex operator+(const Complex& c1, const Complex& c2);
};Complex operator+(const Complex& c1, const Complex& c2) {return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
這里的?operator+
?函數是重載的?+
?運算符函數,它有兩個參數?c1
?和?c2
?,分別代表?+
?運算符左右兩邊的?Complex
?類對象。當執行?Complex c3 = c1 + c2;
?這樣的表達式時,c1
?傳遞給第一個參數,c2
?傳遞給第二個參數,函數執行復數相加的操作并返回結果。
成員函數形式的運算符重載
如果運算符重載函數是成員函數,那么第一個運算對象會默認通過?this
?指針隱式傳遞,所以參數個數比運算對象的數量少一個。
還是以?Complex
?類的?+
?運算符重載為例,用成員函數形式來實現:
class Complex {
private:double real;double imag;
public:Complex(double r = 0, double i = 0) : real(r), imag(i) {}// 成員函數重載 + 運算符,二元運算符,但參數比運算對象少一個Complex operator+(const Complex& other) {return Complex(real + other.real, imag + other.imag);}
};
在這個成員函數形式的?operator+
?中,只有一個參數?other
?。當執行?Complex c3 = c1 + c2;
?時,c1
?是調用該成員函數的對象,通過?this
?指針隱式傳遞,c2
?傳遞給?other
?參數。函數內部通過?this
?指針訪問調用對象(c1
?)的成員變量?real
?和?imag
?,并與?other
?(c2
?)的成員變量進行相加運算。