② 位模式與編碼方案;
③ 可以適用的操作(能夠使用的運算符);
例如double類型,使用8個字節(jié)的內(nèi)存空間,使用IEEE754的編譯方案(1個符號位,11個指數(shù)位使用移碼,52個小數(shù)位),double可以使用+-*/等運算符,但不能使用模運算符%。
一個操作符的運算可以理解為一個函數(shù)形式:
a+b;operator+(a,b);
自然,如果類類型要使用運算符,需要明確定義操作符操作數(shù)據(jù)成員的邏輯(也就是重載操作符),否則,會出現(xiàn)編譯錯誤。
需要注意的是,運算符(operator)重載要求必須至少有一個類類型參數(shù)(或兩個類類型參數(shù)),如果參數(shù)一個都不是類類型,則也就沒有必要重載。對于第一個操作數(shù)(operand,出現(xiàn)在操作符的左邊)的類型也是有限制的。
例如運算符[]要求第一個操作數(shù)只能是類對象,所以需要重載[]為成員函數(shù)(其第一個參數(shù)是隱含的this指針,指向類對象)。
運算符<<要求第一個操作數(shù)為std::ostream類型對象,所以不能重載為成員函數(shù),可以重載為友元函數(shù)。
1 重載雙目運算符[]為成員函數(shù)
雙目運算符[]限制了第一個參數(shù)必須為類類型,所以要重載為成員函數(shù)(第一個參數(shù)為類類型指針this)。
#include class IntList{private: int m_list[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // give this class some initial state for this examplepublic: int& operator[] (int index); int operator[] (int index) const; // could also return const int& // if returning a non-fundamental type, use as a const argument};int& IntList::operator[] (int index) // for non-const objects: can be used for assignment{ return m_list[index];}int IntList::operator[] (int index) const // for const objects: can only be used for access{ return m_list[index];}int main(){ IntList list{}; list[2] = 3; // okay: calls non-const version of operator[] std::cout << list[2] << ''; const IntList clist{}; clist[2] = 3; // compile error: calls const version of operator[], which returns by value. Cannot assign to this because it is an rvalue. std::cout << clist[2] << ''; return 0;}
2 重載雙目運算符<<為友元函數(shù)
雙目運算符<<的第一個參數(shù)必須為std::ostream類型對象,為避免第一個對象為this指針,所以要避免重載為成員函數(shù),可以重載為友元函數(shù)。
#include class Point{private: double m_x {}, m_y {}, m_z {};public: Point(double x=0.0, double y=0.0, double z=0.0) : m_x { x }, m_y { y }, m_z { z } { } friend std::ostream& operator<< (std::ostream &out, const Point &point);};std::ostream& operator<< (std::ostream &out, const Point &point){ // Since operator<< is a friend of the Point class, we can access Point's members directly. out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ")"; return out;}int main(){ Point point1 { 2.0, 3.0, 4.0 }; std::cout << point1; return 0;}
3 重載單目運算符++、–為成員函數(shù)
class Digit{private: int m_digit;public: Digit(int digit=0) : m_digit{digit} { } Digit& operator++(); // prefix has no parameter Digit& operator–(); // prefix has no parameter Digit operator++(int); // postfix has an int parameter Digit operator–(int); // postfix has an int parameter friend std::ostream& operator<< (std::ostream& out, const Digit& d);};// No parameter means this is prefix operator++Digit& Digit::operator++(){ // If our number is already at 9, wrap around to 0 if (m_digit == 9) m_digit = 0; // otherwise just increment to next number else ++m_digit; return *this;}// No parameter means this is prefix operator–Digit& Digit::operator–(){ // If our number is already at 0, wrap around to 9 if (m_digit == 0) m_digit = 9; // otherwise just decrement to next number else –m_digit; return *this;}// int parameter means this is postfix operator++Digit Digit::operator++(int){ // Create a temporary variable with our current digit Digit temp{*this}; // Use prefix operator to increment this digit ++(*this); // apply operator // return temporary result return temp; // return saved state}// int parameter means this is postfix operator–Digit Digit::operator–(int){ // Create a temporary variable with our current digit Digit temp{*this}; // Use prefix operator to decrement this digit –(*this); // apply operator // return temporary result return temp; // return saved state}std::ostream& operator<< (std::ostream& out, const Digit& d){out << d.m_digit;return out;}int main(){ Digit digit(5); std::cout << digit; std::cout << ++digit; // calls Digit::operator++(); std::cout << digit++; // calls Digit::operator++(int); std::cout << digit; std::cout << –digit; // calls Digit::operator–(); std::cout << digit–; // calls Digit::operator–(int); std::cout << digit; return 0;}
4 重載函數(shù)調(diào)用符()為成員函數(shù)
#include class Accumulator{private: int m_counter{ 0 };public: int operator() (int i) { return (m_counter += i); }};int main(){ Accumulator acc{}; std::cout << acc(10) << ''; // prints 10 std::cout << acc(20) << ''; // prints 30 return 0;}
You may wonder why we couldn’t do the same thing with a normal function and a static local variable to preserve data between function calls. We could, but because functions only have one global instance, we’d be limited to using it for one thing at a time. With functors, we can instantiate as many separate functor objects as we need and use them all simultaneously.
您可能想知道為什么我們不能用普通函數(shù)和靜態(tài)局部變量來保存函數(shù)調(diào)用之間的數(shù)據(jù)。我們可以,但因為函數(shù)只有一個全局實例,所以我們只能一次使用一個實例。使用函數(shù)對象,我們可以根據(jù)需要實例化任意多個單獨的函數(shù)對象,并同時使用它們。
Operator() is sometimes overloaded with two parameters to index multidimensional arrays, or to retrieve a subset of a one dimensional array (with the two parameters defining the subset to return). Anything else is probably better written as a member function with a more descriptive name.
操作符()有時會重載兩個參數(shù)來索引多維數(shù)組,或檢索一維數(shù)組的子集(兩個參數(shù)定義要返回的子集)。其他任何內(nèi)容都最好用更具描述性的名稱作為成員函數(shù)編寫。
5 重載賦值運算符=為成員函數(shù)
MyString& MyString::operator= (const MyString& str){// self-assignment checkif (this == &str)return *this;// if data exists in the current string, delete itif (m_data) delete[] m_data;m_length = str.m_length;// copy the data from str to the implicit objectm_data = new char[str.m_length];for (int i { 0 }; i < str.m_length; ++i)m_data[i] = str.m_data[i];// return the existing object so we can chain this operatorreturn *this;}
6 When to use a normal, friend, or member function overload
何時使用普通、友元或成員函數(shù)重載
In most cases, the language leaves it up to you to determine whether you want to use the normal/friend or member function version of the overload. However, one of the two is usually a better choice than the other.
在大多數(shù)情況下,該語言將由您決定是否要使用重載的普通/友元或成員函數(shù)版本。然而,兩者中的一個通常比另一個更好。
When dealing with binary operators that don’t modify the left operand (e.g. operator+), the normal or friend function version is typically preferred, because it works for all parameter types (even when the left operand isn’t a class object, or is a class that is not modifiable). The normal or friend function version has the added benefit of “symmetry”, as all operands become explicit parameters (instead of the left operand becoming *this and the right operand becoming an explicit parameter).
處理不修改左操作數(shù)的二元運算符(例如運算符+)時,通常首選普通或友元函數(shù)版本,因為它適用于所有參數(shù)類型(即使左操作數(shù)不是類對象,或是不可修改的類)。普通或友元函數(shù)版本具有“對稱”的額外好處,因為所有操作數(shù)都成為顯式參數(shù)(而不是左操作數(shù)變成*this,右操作數(shù)變成顯式參數(shù))。
When dealing with binary operators that do modify the left operand (e.g. operator+=), the member function version is typically preferred. In these cases, the leftmost operand will always be a class type, and having the object being modified become the one pointed to by *this is natural. Because the rightmost operand becomes an explicit parameter, there’s no confusion over who is getting modified and who is getting evaluated.
處理確實修改左操作數(shù)的二進制運算符(例如運算符+=)時,通常首選成員函數(shù)版本。在這些情況下,最左邊的操作數(shù)將始終是類類型,并且將要修改的對象變?yōu)?所指的對象是很自然的。因為最右邊的操作數(shù)成為顯式參數(shù),所以對于誰要修改誰來求值沒有任何混淆。
Unary operators are usually overloaded as member functions as well, since the member version has no parameters.
一元運算符通常也作為成員函數(shù)重載,因為成員版本沒有參數(shù)。
The following rules of thumb can help you determine which form is best for a given situation:
以下經(jīng)驗法則可以幫助您確定哪種形式最適合特定情況:
If you’re overloading assignment (=), subscript ([]), function call (()), or member selection (->), do so as a member function.
如果要重載賦值(=)、下標([])、函數(shù)調(diào)用(())或成員選擇(->),請作為成員函數(shù)進行重載。
If you’re overloading a unary operator, do so as a member function.
如果要重載一元運算符,請作為成員函數(shù)進行重載。
If you’re overloading a binary operator that does not modify its left operand (e.g. operator+), do so as a normal function (preferred) or friend function.
如果要重載不修改其左操作數(shù)的二元運算符(例如運算符+),請將其作為普通函數(shù)(首選)或友元函數(shù)進行重載。
If you’re overloading a binary operator that modifies its left operand, but you can’t add members to the class definition of the left operand (e.g. operator<<, which has a left operand of type ostream), do so as a normal function (preferred) or friend function.
如果要重載修改其左操作數(shù)的二元運算符,但不能將成員添加到左操作數(shù)的類定義中(例如運算符<<,其左操作數(shù)類型為ostream),請將其作為普通函數(shù)(首選)或友元函數(shù)。
If you’re overloading a binary operator that modifies its left operand (e.g. operator+=), and you can modify the definition of the left operand, do so as a member function.
如果要重載修改其左操作數(shù)的二元運算符(例如運算符+=),并且可以修改左操作數(shù)的定義,請作為成員函數(shù)進行修改。
-End-