14章 操作符重载和转换
重载操作符是具有特殊名称的函数:保留字operator后接需要定义的操作符符号。
1、重载的操作符名:
+ – * / % ^ & | ~ ! , = < > <= >= ++ – << >> == != && ||等
不能重载的操作符: :: * . ?
2、 重载操作符 必须具有一个类类型操作数。
int operator+(int,int) ;//error : cannot defined built-in operator for ints
3、优先级和结合性是固定的
4、不再具备短路求值特性(重载&&和||不是一种好的做法)
5、类成员和非成员
大多数重载操作符可以定义为普通非成员函数或类的成员函数。
注解: 作为类成员的冲在函数,其形参看起来比操作数数目少1。作为成员函数的操作符有一个隐含的this形参,限定为第一个操作数。
ps: 一般将算术和关系运算定义为非成员函数,而将赋值操作符定义为成员。
// member binary operator: left-hand operand bound to implicit this pointer Sales_item& Sales_item::operator+=(const Sales_item&); //nonmember binary operator: must declare a parameter for each operand Sales_item operator+(const Sales_item&,const Sales_item&);
6、操作符重载和友元关系
操作符定义为非成员函数时,通常必须将它们设置为所操作类的友元。
最佳实践:当一个重载操作符的含义不明显时,给操作取一个名字更好。对于很少用的操作使用命名函数通常比用操作符更好。如果不是普通操作,没必要为简洁而使用操作符。
输出操作符
std::ostream& operator<<(std::ostream& os,const Sales_item& s){ os<<s.isbn<<" "<<s.units_sold<<" " <<s.revenue<<" "<<s.avg_price(); return os; }
最佳实践:
一般而言,输出操作符应输出对象的内容,进行最小限度的格式化,它们不应该输出换行符。
IO操作符必须为非成员函数
friend std::istream& operator>>(std::istream&,Sales_item&); friend std::ostream& operator<<(std::ostream&,const Sales_item&);
输入操作符:
注意: 输入操作的第二形参必须为非const引用,而且输入操作符必须处理错误和文件借宿的可能。
std::istream& operator>>(std::istream& in,Sales_item& s){ double price; in>>s.isbn>>s.units_sold>>price; if(in) s.revenue = s.units_sold * price; else s = Sales_item();//input failed, reset object to default state return in; }
算术操作符和关系操作符
Sales_item& Sales_item::operator+=(const Sales_item& rhs){ if (isbn == rhs.isbn){ units_sold += rhs.units_sold; revenue += rhs.revenue; } return *this; } Sales_item operator+(const Sales_item& s1,const Sales_item& s2){ Sales_item ret(s1); ret += s2;//use operator+= return ret; } inline bool operator==(const Sales_item& lhs,const Sales_item& rhs){ return lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue && lhs.same_isbn(rhs); } inline bool operator!=(const Sales_item& lhs,const Sales_item& rhs){ return !(lhs==rhs);//!= defined interm of operator== }
最佳实践:
为了与内置操作符保持一致,加法返回一个右值,而不是一个引用。
既定义了算术操作又定义了相关复合赋值操作的类,一般应使用复合操作实现算术操作符。
赋值操作符必须返回*this的引用
下标操作符:
#ifndef FOO_H #define FOO_H #include <vector> using std::vector; class Foo{ public: int &operator[](const size_t); const int &operator[](const size_t); private: vector<int> data; }; int& Foo::operator [](const size_t index){ return data[index];//no range checking on index } const int& Foo::operator [](const size_t index){ return data[index];//no range checking on index } #endif // !FOO_H
下标操作符必须定义为类成员函数
类定义下标操作符时,一般需要定义两个版本,一个为非const成员并返回引用,另一个为const成员并返回const引用。
成员访问操作符(* –>)
注解: 箭头操作符必须定义为类成员函数,解引用操作符不要定义为成员,但将它作为成员一般也是正确的。
1、构建更安全的指针
#ifndef SCRPTR_H #define SCRPTR_H #include "Person.h" #include <iostream> class ScrPtr{ friend class ScreenPtr; Screen *sp; size_t use; ScrPtr(Screen *p):sp(p),use(1){} ~ScrPtr(){delete sp;} }; class ScreenPtr{ public: ScreenPtr(Screen* p):ptr(new ScrPtr(p)){} ScreenPtr(const ScreenPtr& orig):ptr(orig.ptr){++ptr->use;} ScreenPtr& operator=(const ScreenPtr&); ~ScreenPtr(){ if(--ptr->use = 0) delete ptr; } //两个版本 const和非const Screen& operator*(){return *ptr->sp;} Screen* operator->(){return ptr->sp;} const Screen& operator*()const{return *ptr->sp;} const Screen* operator->()const{return ptr->sp;} private: ScrPtr *ptr; }; ScreenPtr& ScreenPtr::operator=(const ScreenPtr& rhs){ ++rhs.ptr->use; if(--ptr->use == 0) delete ptr; ptr = rhs.ptr; return *this;//注意赋值 复合赋值操作符必须返回*this } void scrptrTest(){ Screen myscreen; ScreenPtr p(&myscreen); p->display(std::cout); } #endif // !SCRPTR_H
注解:重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己箭头操作符的类类型对象。
自增操作符和自减操作符
#ifndef CHECKED_PTR_H #define CHECKED_PTR_H /* * smart pointer: Checks access to elements throws an out_of_range * exception if attempt to access a nonexistent element * * user allocate and free the array */ class CheckedPtr{ public: CheckedPtr(int* b, int* e):beg(b),end(e),curr(b){} //prefix operators CheckedPtr& operator++(); CheckedPtr& operator--(); //postfix operators CheckedPtr operator++(int); CheckedPtr operator--(int); private: int* beg; int* end; int* curr; }; CheckedPtr& CheckedPtr::operator ++(){ if(curr == end) throw out_of_range ("increment past the end of CheckedPtr"); ++curr; return *this; } CheckedPtr& CheckedPtr::operator --(){ if(curr == beg) throw out_of_range("decrement past the beginning of CheckedPtr"); --curr; return *this; } CheckedPtr CheckedPtr::operator ++(int){ CheckedPtr ret(*this); ++*this; return ret; } CheckedPtr CheckedPtr::operator --(int){ CheckedPtr ret(*this); --*this; return ret; } #endif // !CHECKED_PTR_H
调用操作符和函数对象
struct absInt { int operator()(int val){ return val < 0? -val : val; } }; void absInt_test(){ int i = -32; absInt absObj; using int ui = absObj(i); }
注解:函数调用操作符必须声明为成员函数。一个类可以定义函数调用操作符的多个版本。有形参的数目或类型加以区别。