重载前须知

重载运算符是特殊的函数,它们的名字由operator和其后要重载的运算符号共同组成。 因为重载运算符时函数, 因此它包含返回值、参数列表和函数体。
对于重载运算符是成员函数时, 它的第一个运算对象被隐式的绑定到this指针上,因此,成员函数的重载运算符的显示参数数量比运算符的运算对象少一个。

对一个运算符函数来说, 要么它是一个类的成员函数, 或者它的参数至少包含一个类类型。


某些运算符不应该被重载

对于逻辑与&&、逻辑或 || 和逗号运算符来说,重载它们会无法保留下来它们的运算对象的求值顺序。 而且对于&& 和 || 来说,它们具有的短路求值属性也无法保留。

对于取地址运算符,它又特定的内置含义,它也不该被重载。


重载运算符应该和内置类型一样的含义
  • 如果类执行IO操作,则定义移位运算符使其与内置类的IO 一致。
  • 一般定义了相等性运算符==,那么也应该定义!= 运算符。
  • 一个类定义了一个比较运算符,那么它也应该定义其他比较运算符。
  • 重载运算符的返回类型应该和内置版本的返回类型一致。

选择座位成员还是非成员函数

  • 赋值(=)、 下标([])、调用(())和成员访问箭头运算符必须定义为成员函数
  • 复合赋值运算符一般定义为成员函数,但不是必须的
  • 改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符,一般定义为成员函数
  • 具有对称性的运算符可能转换任意一端的运算对象,例如算术、相等性、关系和位运算符等,通常应该为非成员函数。

输入和输出运算符

输入、输出运算符必须是非成员函数, 一般被定义为类的友元!

重载输出运算符

输出运算符的第一个形参是一个非常量的ostream对象的引用,第二个形参一般是一个常量的引用,因为输出运算符不会改变参数的值。
operator << 一般返回它的ostream形参。

Sales_data的输出运算符:

ostream& operator << (ostream& os, const Sales_data& item) {
os << item.isbn() << " " << item.units_sold << " " <<item.revenue << " " << item.avg_price();
return os; }

值得注意的是,为了和内置类型的输出运算符保持一致,我们重载的输出运算符应该尽量减少格式化操作, 尤其是换行符!


重载输入运算符

输入运算符的第一个参数应该是一个要读的流的引用,第二个参数形参应该是一个非常量的对象的引用,它返回输入流的引用。
输入运算符应该处理可能输入失败的情况!


Sales_data的输入操作:

istream& operator >> (istream& is, Sales_data& item) {

	double price;
is >> item.bookNo >> item.units_sold >> price;
if (is) // 检测输入流
item.revenue = item.units_sold * price;
else
item = Sales_data(); // 输入失败,对象被赋予默认状态 return is;
}

正如程序中看到的,重载的输入运算符应该要处理可能输入失败的情况,当读取失败时,输入运算符应该负责从错误中恢复。


算术和关系运算符

通常情况下,我们将算术和关系运算符定义为非成员函数,以允许向左侧或右侧的运算对象进行转换。

算术运算符

一般的,如果定义了算术运算符,则它一般也需要定义一个对应的复合赋值运算符。

Sales_data operator + (const Sales_data& lhs, const Sales_data& rhs) {

	Sales_data sum = lhs;
sum += rhs;
return sum;
}

同时定义了算术运算符和相应的复合赋值运算符,则一般用复合赋值运算符实现算术运算符。



相等运算符

判断两个类是否相等时,我们应该比较它的所有成员:
bool operator == (const Sales_data& lhs, const Sales_data& rhs) {

	return lhs.isbn() == rhs.isbn() &&
lhs.units_sold == rhs.units_sold &&
lhs.revenue == rhs.revenue;
} bool operator != (const Sales_data& lhs, const Sales_data& rhs) { return !(lhs == rhs);
}

如果定义了==, 则运算符应该判断给定的两个对象是否含有重复数据。
==应该具有传递性,如果 a == b, b == c, 则 a == c。
如果定义了==,则我们也应该定义 !=
相等运算符和不等运算符中的一个应该把工作委托给另一个。


关系运算符

一般定义了相等运算符的类,也应该定义关系运算符,特别的是,关联容器和一些算法需要用到小于运算符,因此定义operator < 比较有用。
关系运算符应该:1. 定义顺序关系,令其与关联容器对关键字的要求一样,严格弱序。 2. 如果类同时有 == 运算符,则定义一种关系令其与==保持一致,如果两个对象是!= 的,那么一个对象应该 < 另一个。

存在唯一的逻辑可靠的 < 定义,才考虑为一个类定义 < 运算符。如果类同时定义了==, 当且仅当<的定义与==产生的结果一致时才定义<运算符。


赋值运算符

赋值运算符必须定义为成员函数。
重载赋值运算符应该与内置类型的赋值运算符保持一致,应该返回左则运算对象的引用:

StrVec& StrVec::operator = (initializer_list<string> il) {

	auto data = alloc_n_copy(il.begin(), il.end());
free();
first = data.first;
last_end = cap = data.last_end;
return *this;
}

赋值运算符应该先释放左则对象的内存空间。


复合赋值运算符

复合赋值运算符不一定是类的成员函数,不过我们应该把包括复合赋值在内的所有赋值运算都定义在类的内部。复合赋值运算符也要返回左侧运算对象的引用:
Sales_data& Sales_data::operator += (const Sales_data& rhs) {

	units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}


下标运算符

下表运算符必须是成员函数。

下标运算符应该以所访问的元素的引用作为返回值,而且我们最好定义常量版本和非常量版本。、

class StrVec {
public:
std::string& operator [] (size_t n) { return first[n]; }
const std::string& operator [] (size_t n) const { return first[n]; }
private:
std::string* first; // 指向数组的首元素
};

递增和递减运算符

递增和递减运算符通常应该定义为类的成员函数。

前置版本:

class StrBlobPtr {
public:
StrBlobPtr& operator++();
StrBlobPtr& operator--();
//
};

前置版本的递增/递减运算符应该返回递增或递减后的对象的引用。



后置版本:
class StrBlobPtr {
public:
StrBlobPtr operator++(int);
StrBlobPtr operator--(int);
//
};

为了前置版本和后置版本,将后置版本中的参数列表中添加一个int型参数,但是这个int型只是用来区分前置和后置版本的运算符,并不使用它。

后置版本的递增/递减运算符应该返回递增/递减前的对象的值。


成员访问运算符

箭头运算符必须是类的成员, 解引用也应该是类的成员,尽管并非如此。


class StrBlobPtr {
public:
std::string& operator*() const {
auto p = check(curr, "dreference past end");
return (*p)[curr];
} std::string* operator->()const {
return & this->operator*();
}
//
};

对箭头运算符返回值的限定

箭头运算符永远不能丢掉获取成员这一事实。

对于形如point->mem的表达式,point必须是指向类对象的指针或者是一个重载了operator->的类的对象,根据point类型不同,可分为两种情况:
(*point).mem; // (1)
point.operator()->mem; // (2)

如果point是指针,则应该使用内置的箭头运算符,表达式等价于上面的第一条。

如果point是定义了operator->的类的一个对象,则使用operator->的结果来获取mem。如果该结果是一个指针,则执行第一步,如果该结果本身含有重载的operator->(), 则重复调用当前步骤。


重载的箭头运算符必须返回类的指针或自定义了箭头运算符的某个类的对象。



C++ Primer : 第十四章 : 重载运算与类型转换之重载运算符的更多相关文章

  1. C++ Primer : : 第十四章 : 重载运算符与类型转换之类型转换运算符和重载匹配

    类型转换运算符 class SmallInt { public: SmallInt(int i = 0) : val(i) { if (i < 0 || i > 255) throw st ...

  2. C++Primer 第十四章

    //1.当运算符作用于类类型运算对象时,可以通过运算符重载重新定义该运算符的含义.明智的使用运算符重载能令程序更加易于编写和阅读. //2.重载的运算符是具有特殊名字的函数,它们由关键字operato ...

  3. 【C++】《C++ Primer 》第十四章

    第十四章 重载运算与类型转换 一.基本概念 重载运算符是具有特殊名字的函数:由关键字operator和其后要定义的运算符号共同组成.也包含返回类型.参数列表以及函数体. 当一个重载的运算符是成员函数时 ...

  4. C++ Primer Plus学习:第十四章

    第十四章 C++中的代码重用 包含对象成员的类 将类的对象作为新类的成员.称为has-a关系.使用公有继承的时候,类可以继承接口,可能还有实现(纯虚函数不提供实现,只提供接口).使用包含时,可以获得实 ...

  5. 《Linux命令行与shell脚本编程大全》 第十四章 学习笔记

    第十四章:呈现数据 理解输入与输出 标准文件描述符 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 1.STDIN 代表标准输入.对于终端界面 ...

  6. perl 第十四章 Perl5的包和模块

    第十四章 Perl5的包和模块 by flamephoenix 一.require函数  1.require函数和子程序库  2.用require指定Perl版本二.包  1.包的定义  2.在包间切 ...

  7. 第十四章——循环神经网络(Recurrent Neural Networks)(第一部分)

    由于本章过长,分为两个部分,这是第一部分. 这几年提到RNN,一般指Recurrent Neural Networks,至于翻译成循环神经网络还是递归神经网络都可以.wiki上面把Recurrent ...

  8. 第十四章——循环神经网络(Recurrent Neural Networks)(第二部分)

    本章共两部分,这是第二部分: 第十四章--循环神经网络(Recurrent Neural Networks)(第一部分) 第十四章--循环神经网络(Recurrent Neural Networks) ...

  9. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段 代码工程地址: https://github. ...

随机推荐

  1. 配置rt-thread开发环境(配置系统,生成系统镜像)

    配置rt-thread开发环境 ===========Python============= 1.Python的下载地址:http://www.python.org/ftp/python/ 链接中有各 ...

  2. JQuery text()、html() 以及 val()

    获得内容 - text().html() 以及 val() 三个简单实用的用于 DOM 操作的 jQuery 方法: text() - 设置或返回所选元素的文本内容 html() - 设置或返回所选元 ...

  3. JS判断数组中是否有重复元素的方法

    判断数组中是否有重复元素,最容易想到的方法是使用2重循环,逐个遍历,比较,但是这个是最慢,最笨的方法,百度得出了更好的方法. var ary = new Array("111",& ...

  4. SPSS数据分析—卡方检验

    t检验和方差分析主要针对于连续变量,秩和检验主要针对有序分类变量,而卡方检验主要针对无序分类变量(也可以用于连续变量,但需要做离散化处理),用途同样非常广泛,基于卡方统计量也衍生出来很多统计方法. 卡 ...

  5. ITPUB网站的知识索引汇总

    1. ITPUB知识索引树 http://www.itpub.net/tree/ http://www.itpub.net/pubtree/index.htm 2. ITPUB知识索引贴——全文索引 ...

  6. ios批量打包

    最近我们接到了新的需求,需要打出类似xx001-xx100共100个这样的ipa渠道包,不需要签名.(这批ipa包后续会用企业证书签名,不会影响AppStore的) 这些包所有的功能.内容都是一样的, ...

  7. Windows下Apache的优化

    (1)首选查看apache的工作模式 windows下的查看apache的工作模式命令: httpd -l 如果列出mod_win32.c,则表示是 win32.c 工作方式. 列出的全部内容如下所示 ...

  8. jquery替换URL参数值

    由于经常会用到替换URL参数值,而网上写的方法代码都太长了,所以在这里写了一个简单的方法,供大家使用. 说明: reLoad(参数名,参数值) function reLoad(p, v) { var ...

  9. 使用函数库(JAVA API)

    /*使用函数库(JAVA API) * 在JAVA的API里类被封装在一个个的package,要使用package的类之前必须 * 要知道这个类属于哪个package * 引用类方式: * 1.通过i ...

  10. fifo read

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types. ...