1、运算符重载限制
四种不可重载的运算符,它们是. .* :: ?:
运算符重载不可以改变运算符运算优先级;
运算符重载不可以改变运算符的结合律(从右到左结合或从左到右结合);
运算符重载不可以改变运算符的元数(二元运算符重载之后仍然是二元运算符);
运算符重载不可以创造新的运算符。
2、运算符重载函数的使用
运算符函数可以是类的成员函数,也可以全局函数。出于性能方面的考虑,全局运算符函数一般定义为友元函数。如果要重载()、[]、->或者任何赋值运算符,那么运算符函数必须声明为类的成员函数。如果运算符的左操作数必须是不同类的对象(也就是说如果我们想把运算符函数声明为类A的成员函数,但这个运算符的左操作数是类B的对象)或者是一个基本类型对象,那么运算符函数必须声明为全局函数。例如,重载(流插入符>>和)流提取符<<时,在函数operator<<()中cout<<classObject语句中<<流提取符的左操作数是ostream
&,并不是我们期望的类的对象classObject,这时候operator<<()要声明为全局函数。具体见本文最后的例子。
综上,运算符函数定义为类的成员函数仅适用于以下两种情况:第一,当二元运算符的左操作数确实是该类的对象时;第二,当一元运算符的操作数为该类的对象时。
3、示例代码
示例一:(注意看下面连个程序的第29行和第33行,尤其是第二个程序的33行运算符函数参数次序问题)
因为我们把<的第一个操作数为当前类的对象,所以运算符函数被定义为成员函数
#include <iostream> #include <string> using namespace std; class A { private: string str; public: A(string str); bool operator<(const A& a); }; A::A(string str) { this->str = str; } bool A::operator<(const A& a) { if(this->str < a.str)//成员函数可以直接访问private变量 return true; else return false; } int main() { A a1("abcde"),a2("abcde"); if(a1<a2)//会被处理为a1.operator<(a2) cout<<"a1<a2"<<endl; else cout<<"a1>=a2"<<endl; return 0; }
下面我们看看运算符函数被定义为全局函数的情况 (特别注意第33行a1和a2在运算符函数中的次序分配问题)
#include <iostream> #include <string> using namespace std; class A { private: string str; public: A(string str); string getStr() const; friend bool operator<(const A& a, const A& b); }; A::A(string str) { this->str = str; } string A::getStr() const { return this->str; } bool operator<(const A& a, const A& b) { if(a.getStr() < b.getStr())//非成员函数不可以直接访问private变量 return true; else return false; } int main() { A a1("abcdf"),a2("abcde"); if(a1<a2)//会被处理为operator<(a1,a2) cout<<"a1<a2"<<endl; else cout<<"a1>=a2"<<endl; return 0; }
示例二:(重点看运算符<<的重载)
#include <iostream> #include <deque> #include <stdexcept> using namespace std; #define EXIT_FAILURE -1 template <typename T> class CLStack { private: deque<T> elems; public: void push(const T& e); void pop(); T top() const; bool isEmpty() const; template <typename T2> CLStack<T> & operator=(const CLStack<T2> &stack); //因为friend函数是类外函数,所以模板参数要用U防止与T冲突 template <typename U> friend ostream &operator<<(ostream &output, CLStack<U> &stack); }; template <typename T> void CLStack<T>::push(const T& e) { elems.push_back(e); } template <typename T> bool CLStack<T>::isEmpty() const { return elems.empty(); } template <typename T> void CLStack<T>::pop() { if(isEmpty()) { throw std::out_of_range("Stack<>::pop: empty stack"); } elems.pop_back(); // 移除最后一个元素 } template <typename T> T CLStack<T>::top() const { if(isEmpty()) { throw std::out_of_range("Stack<>::top: empty stack"); } return elems.back(); } template <typename T> template <typename T2> CLStack<T>& CLStack<T>::operator=(const CLStack<T2>& stack) { if((void *)this == (void *)&stack) return *this; CLStack<T2> tmp(stack); //拷贝构造函数 elems.clear(); while(!tmp.isEmpty()) { elems.push_front(tmp.top()); tmp.pop(); } return *this; } template <typename U> ostream &operator<<(ostream &output, CLStack<U> &stack) { while(!stack.isEmpty()) { output<<stack.top()<<" "; stack.pop(); } output<<endl; return output; } int main() { CLStack<int> int1Stack,int2Stack; int1Stack.push(1); int2Stack = int1Stack; try{ cout<<int2Stack.top()<<endl; // int2Stack.pop(); // int2Stack.pop(); } catch(const exception& ex) { cerr<<"Catch Exception: "<<ex.what()<<endl; return EXIT_FAILURE; // 传回错误状态码 } // cout<<int2Stack.pop()<<endl; CLStack<float> floatStack; floatStack = int2Stack; cout<<floatStack.top()<<endl; floatStack.push(2); cout << floatStack; return 0; }