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;
}