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. 视频相关android软件

    1. 视频解码工具:ffmpeg, http://www.ffmpeg.org/ 2. java有一个开源程序: yoyoPlayer, 可以到这个代码中去学习相关的音频知识.http://www.b ...

  2. 用C#编写游戏脚本

    大学宿舍玩游戏的时候,为了简化重复的键鼠动作,有学习过按键精灵和TC脚本开发工具,并做了一些小脚本,基本达到了当时的需求.不知不觉,已经毕业了3年了,无聊之余又玩起了游戏,对于一些无趣的重复行为,于是 ...

  3. Orchard官方文档翻译(七) 导航与菜单

    原文地址:http://docs.orchardproject.net/Documentation/Navigation-and-menus 想要查看文档目录请用力点击这里 最近想要学习了解orcha ...

  4. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part4:强制Move语义

    本文为第四部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/4220233.html. 强制Move语义 众所周知,正如C++标准的第一修正案所陈述:“委 ...

  5. 大陆地区OpenStack项目Core现状(截至2016年1月28日,转载自陈沙克日志)

    陈沙克 经常有朋友问,大陆地区大概有多少位OpenStack项目的Core.这个问题,现在其实不太好回答,如果需要准确统计的话.下面仅仅是一个大概估计,有遗漏的,希望朋友指出,我来补全. 文档修改历史 ...

  6. PMP考试--成本管理中常用的概念

    如果你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",加入这个PM.架构师的大家庭 净现值(NPV)   Net Present Value 在项目计算期内,按行业基准 ...

  7. Windows 7的100M隐藏分区

    1.Windows 7的100MB的隐藏分区是Windows 7的活动分区,类似于Linux的/boot. 这其实有点类似Linux的做法,Linux在安装过程中可以专门分出一个100MB左右的分区作 ...

  8. 解决ScrollView下嵌套ListView、GridView显示不全的问题

    /** * 自定义gridview,解决ListView中嵌套gridview显示不正常的问题(1行半) * @author wangyx * @version 1.0.0 2012-9-14 */ ...

  9. android 3D旋转效果实现

    一说到3D,可能第一反应就是使用OpenGL ES....但是,实现这么个小功能,要动用这玩意,莫名的恐惧啊!!!!至今也没弄明白这个怎么玩... 好吧,幸亏还有个Camera类可以帮助我们,据说底层 ...

  10. 【Struts 2】Struts2环境搭建

    一.关键步骤 1.创建Java Web项目 2.引入Struts2的依赖包,将依赖包拷贝到WEB-INF/lib下 * commons-logging-1.0.4.jar * freemarker-2 ...