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

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

C++primer 阅读点滴记录(三)的更多相关文章

  1. C++primer 阅读点滴记录(一)

    第十三章 复制控制:(copy control) 复制构造函数(copy constructor) 复制操作符(assignment operator) ps: 什么时候需要显示的定义复制控制操作:类 ...

  2. C++primer 阅读点滴记录(二)

      智能指针(smart point)       除了增加功能外,其行为像普通指针一样. 一般通过使用计数(use count)或引用计数(reference count)实现智能指针,防止出现指针 ...

  3. C++PRIMER 阅读笔记 第三章

    本章主要介绍 string vector 和 bitset, 不能贪多,现在本文主要介绍 string 与 vector 头文件中最好不要使用namespace std, 因为头文件会直接被预处理器放 ...

  4. 《大象Think in UML》阅读笔记(三)

    Think in UML 阅读笔记(三) 把从现实世界中记录下来的原始需求信息,再换成一种可以知道开发的表达方式.UML通过被称为之概念化的过程来建立适合计算机理解和实现的模型,这个模型被称为分析模型 ...

  5. Hadoop阅读笔记(三)——深入MapReduce排序和单表连接

    继上篇了解了使用MapReduce计算平均数以及去重后,我们再来一探MapReduce在排序以及单表关联上的处理方法.在MapReduce系列的第一篇就有说过,MapReduce不仅是一种分布式的计算 ...

  6. {django模型层(二)多表操作}一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询、分组查询、F查询和Q查询

    Django基础五之django模型层(二)多表操作 本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 xxx 七 ...

  7. JavaScript学习记录三

    title: JavaScript学习记录三 toc: true date: 2018-09-14 23:51:22 --<JavaScript高级程序设计(第2版)>学习笔记 要多查阅M ...

  8. grpc使用记录(三)简单异步服务实例

    目录 grpc使用记录(三)简单异步服务实例 1.编写proto文件,定义服务 2.编译proto文件,生成代码 3.编写服务端代码 async_service.cpp async_service2. ...

  9. 3.VUE前端框架学习记录三:Vue组件化编码1

    VUE前端框架学习记录三:Vue组件化编码1文字信息没办法描述清楚,主要看编码Demo里面,有附带完整的代码下载地址,有需要的同学到脑图里面自取.脑图地址http://naotu.baidu.com/ ...

随机推荐

  1. IOS学习之路--BLOCK

    /* 1.定义block变量: 返回值类型 (^block变量名) (参数类型1, 参数类型2, ....); 2.给block变量赋值 block变量名 = ^(参数类型1 参数名称1, ..... ...

  2. ORACLE快速彻底Kill掉的会话(转载)

    转载:http://www.cnblogs.com/kerrycode/p/4034231.html 在ORACLE数据库当中,有时候会使用ALTER SYSTEM KILL SESSION 'sid ...

  3. JAVA·多线程:线程优先级

    每次结果不尽相同,优先级不能完全保证! package multiThread; public class Thread04Priority { public static void main(Str ...

  4. 下载和使用 Open XML PowerTools

    安装 Open XML SDK 2.5 首先,需要安装 Open XML SDK 2.5 ,从这个地址下载安装程序:http://www.microsoft.com/en-in/download/de ...

  5. 【HTTP】POST 与 PUT 方法区别

    1. POST 用于向服务端发送数据,常用于表单数据提交: PUT   用于向服务器上的资源(如文件)中存储数据: 2. 302 303 307 状态码区别 他们都是重定向(临时重定向 p.s 301 ...

  6. Flash图表控件FusionCharts如何在图表标绘非连续数据

    你可能经常要以不完整的数据点绘制图表.例如,当绘制每月的销售图表时,你可能没有所有的月数据.所以,你可能只想以一个空白的区域来显示缺失的数据,不在这个区域中绘制任何东西.FusionCharts可以让 ...

  7. python拷贝文件到多个文件夹

    主要用来做数据备份,每次用完以后再跑一次脚本,又可以将文件夹下的所有文件拷贝到指定的文件夹内 import os,sys,shutil; class cur_env: path = sys.path[ ...

  8. bzoj1216 [HNOI2003]操作系统

    1216: [HNOI2003]操作系统 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 751  Solved: 419[Submit][Status ...

  9. C#中如何判断联系电话的合法性

    string tel = tb_tel.Text.Trim();//联系电话if (!string.IsNullOrEmpty(tb_tel.Text.Trim())){try{//num = Con ...

  10. PAT1015. Reversible Primes

    //题的理解是n在基数b中的的表示中,正序和逆序值都是素数,但是其实可直接判断n,因为n在b中的正常序列值就是再换成十进制就是n,但是不A:不知道为什么 用笨方法,先把n展开成b进制,正常计算其实是翻 ...