C++primer 阅读点滴记录(三)

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<<"\t"<<s.units_sold<<"\t"
<<s.revenue<<"\t"<<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);
}

注解:函数调用操作符必须声明为成员函数。一个类可以定义函数调用操作符的多个版本。有形参的数目或类型加以区别。

上一篇:浅谈:javascript的面向对象编程之具体实现


下一篇:C++primer 阅读点滴记录(二)