20、C++ Primer 4th 笔记,重载运算符(1)
1、除了函数调用符之外,重载操作符的形参数目(包括成员函数的隐式this指针)与操作符的操作数目相同。函数调用操作符可以接受任意数目的操作数。
表1 可重载的操作符名
+ |
- |
* |
/ |
% |
^ |
& |
| |
~ |
! |
, |
= |
< |
> |
<= |
>= |
++ |
-- |
<< |
>> |
== |
!= |
&& |
|| |
+= |
-= |
/= |
%= |
^= |
&= |
|= |
*= |
<<= |
>>= |
[] |
() |
-> |
->* |
new |
new[] |
delete |
delete [] |
表2 不能重载的操作符
:: |
.* |
. |
?: |
不能通过连接其他合法符号来创建任何新的操作符。
2、重载操作符必须具有一个类类型/枚举类型操作数。不能改变内置操作符原有的优先级和结合性。除了函数调用操作符operator()之外,重载操作符时使用默认实参是非法的。
3、作为成员函数的操作符有一个隐含的this指针,限定为第一个操作数。
4、一般将算术和关系操作符定义为非成员函数,而将赋值操作符定义为成员。
5、加返回右值,而复合赋值返回对左操作数引用。
6、重载的设计
1)不要重载具有内置含义的操作符
赋值操作符(可定义自己的)、取地址操作符和逗号操作符对类类型操作数有默认含义。内置逻辑与(&&)和逻辑或(||)操作符使用短路求值,如果重新定义该操作符,将失去操作符的短路求值特征。
2)用作关联容器键类型的类就有<操作符,顺序容器中应定义==操作符。为了相应算法操作的方便。如sort,find等。
3)总结
• 赋值(=)、下标([])、调用(())和成员访问箭头(->)等操作符必须定义为成员,将这些操作符定义为非成员函数将在编译时标记为错误。
• 像赋值一样,复合赋值操作符通常应定义为类的成员,与赋值不同的是,不一定非得这样做,如果定义非成员复合赋值操作符,不会出现编译错误。
• 改变对象状态或与给定类型紧密联系的其他一些操作符,如自增、自减和解引用,通常就定义为类成员。
• 对称的操作符,如算术操作符、相等操作符、关系操作符和位操作符,最好定义为普通非成员函数。
7、重载操作符的定义示例
1)输出<<
示例
ostream& operator<<(ostream& os, const ClassType *object) { //... os << //... return os; }
通常所做格式化应尽量少。
IO操作符必须为非成员函数,否则会出现 item << cout的这种不自然情形。
2)输入>>
输入操作符必须处理错误和文件结束的可能性。
示例
istream& operator>>(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; }
通常输入操作符仅需设置failbit,设置eofbit意思是文件耗尽,设置badbit指出流被破坏。
3)算术操作符和关系操作符
示例
Sales_item operator+(const Salse_item& lhs, const Salse_item& rhs) { Sales_item ret(lhs); ret += rhs; //非常好用的方法 return ret; }
4)相等操作符与不等操作符一起实现
示例
inline bool operator==(const ClassType &lhs, const ClassType &rhs) { return ... } inline bool operator!=(const ClassType &lhs, const ClassType &rhs) { return !(lhs == rhs); }
5)一般而言,赋值操作符与复合赋值操作符应返回左操作数的引用(*this)。
6)下标操作符
定义两个版本:一个为非const成员并返回引用,另一个为const成员并返回const引用。
示例
class Foo { public: int &operator[](const size_t); const int &operator[](const size_t) const; //... private: vector<int> data; }; int& Foo::operator[](const size_t index) { return data[index]; //注意,这里没有进行下标越界检查 } const int& Foo::operator [](const size_t index) const { return data[index]; }
7)成员访问操作符
同样,也是两个版本。
重载箭头操作符必须返回指向类类型的指针,或返回定义了自己的箭头操作符的类类型对象(或引用)。
示例
#include "iostream" #include "stdio.h" using namespace std; class ScreenPtr; class Screen //用于ScreenPtr中操作对象的类 { public: Screen() { cout << "In Screen" << endl; } void display() { cout << "In Screen Display" << endl; } }; class ScrPtr //智能指针,用来管理ScreenPtr类中要操作对象的类的指针 { 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; } cout << "In ScreenPtr" << endl; } public: 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; }; int main() { Screen *myscreen = new Screen(); ScreenPtr p(myscreen); p->display(); return 0; }
说明:point->action();
1.If point is a pointer to a class object that has a member named action , then
the compiler writes code to call the action member of that object.
2.Otherwise, if point is an object of a class that defines operator-> , then
point->action is the same as point.operator->()->action . That is, we execute
operator->() on point and then repeat these three steps, using the result of
executing operator-> on point .
3. Otherwise, the code is in error.