Chapter14:重载运算符
对于一个运算符函数来说,它或者是类的成员,或者至少含有一个类类型的参数。
int operator+(int, int);//错误,不能为int重定义内置运算符
对于一个重载的运算符来说,其优先级和结合律与对应的内置运算符保持一致。
x==y+z
//永远等价于
x == (y+z)
逻辑与运算、逻辑或运算和逗号运算符的运算对象求值顺序规则无法保留下来,所以不建议重载;
逗号运算发和取地址运算符不重载的另一个原因:C++语言已经定义了这两个运算符用于类类型对象时的特殊含义。
尽量明智地使用运算符重载(慎用)
每个运算符在用于内置类型时都有比较明确地含义。当在内置的运算符和我们自己的操作之间存在逻辑映射关系时,运算符重载的效果最好。此时,使用重载的运算符显然比另一个名字更自然也更直观。不过,过分滥用运算符也会使我们的类变得难以理解。
只有当操作的含义对于用户来说清晰明了时才使用运算符。
成员函数&非成员函数
赋值(=),下标([]),调用(())和成员访问箭头(->)运算符必须是成员;
复合赋值一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。
改变对象状态的运算符或者给定类型密切相关的运算符,如递增、递减、和解引用运算符,通常应该是成员;
具有对称性的运算符通常应该是普通的非成员函数。
关于<运算符
Best Practice:如果存在唯一一种逻辑可靠的<定义,则应该考虑为这个类定义<运算符。如果类同时还包含==,则当且仅当<的定义和==产生的结果一致时才定义<运算符。
关于下标运算符
我们最好同时定义下标运算符的常量版本和非常量版本,当作用于一个常量对象时,下标运算符返回常量引用以确保我们不会给返回的对象赋值。
string& operator[] (size_t n);
const string& operator[] (size_t n) const;
关于箭头运算符
point->mem:point必须是指针类型或是重载了operator->的类对象。
根据point的不同,point->mem等价于:
1.(*point).mem;//point是指针类型
2.point.operator()->mem;//point是类的一个对象。
那么这一表达式是如何执行的呢?
如果point是指针,则执行(1);
如果point是类对象,则使用point.operator->()的结果来获取mem;其中,如果该结果是一个指针,则执行第1步;如果该结果本身是重载了operator->()的对象,则重复执行(2)
lambda与函数对象
默认情况下,lambda产生的类不能改变它捕获的变量,所以函数调用操作符是一个const成员函数。
lambda表达式产生的类不含默认构造函数、赋值运算符及默认析构函数,它是否含有默认的拷贝/移动构造函数则通常视捕获的数据成员类型而定。
可调用对象与function
调用形式(call signature):调用返回类型+参数类型就是一种调用形式。所以,两种不同的可调用对象可能共享同一种调用形式。
int add(int i, int j) { return i + j; }
auto mod = [](int i, int j) {return i%j; };
struct divide
{
int operator()(int i, int j) { return i / j; }
};
假设我们要将这些可调用对象统一到函数表中
map<string, int(*)(int, int)> binops;
binops.insert({ "+",add });
binops.insert({ "%",mod });//错误,mod不是函数指针
如何解决这一问题?
function标准库
map<string, function<int(int, int)>> binops;
binops.insert({ "+",add });
binops.insert({ "%",mod });
但是一个问题是:重载函数
int add(int i, int j) { return i + j; }
Sale_data add(const Sale_data&, const Sale_data&);
map<string, function<int(int, int)>> binops;
binops.insert({ "+",add });//哪个add?
两种解决方法:
binops.insert({ "+",fp });
//或者
binops.insert({ "+",[](int a,int b) {return add(a,b); } });
类型转换
之前我们通过转换构造函数来实现了用户定义的类型转换。
现在我们提供另一种方法:定义类型转换运算符。
operator type() const;
一个类型转换函数必须是类的成员函数;它不能声明返回类型,形参列表也必须为空。通常应该是const。
之前我们提到过:编译器一次只能执行一次用户定义的类型转换,但是隐式的用户类型转换可以置于一个标准(内置)类型转换之前或之后。
避免过度使用类型转换函数:除非两种类型之间存在明确地一对一映射。
在实践中,类很少提供类型转换运算符。在大多数情况下,如果类型转换自动发生,用户可能会感觉比较意外,而不是感觉受到了帮助。然而这条经验法则存在一种例外情况:对于类来说,定义向bool的类型转换还是比较普遍的现象。
一个问题:
int i = ;
cin << i;
因为cin没有>>,所以,cin会转换成bool类型,然后左移。
为了防止这一情况的发生,C++新标准引入了显式地类型转换运算符。
该规定存在一个例外:如果表达式被用作条件,则编译器会将显式地类型转换自动应用于它。由此,解决这一问题。
二义性类型转换(要避免)
二义性1:
struct B;
struct A
{
A() = default;
A(const B&);
};
struct B
{
operator A() const;
};
A f(const A&);
B b;
A a = f(b);//究竟是f(B::oprator A())还是f(A::A(const B&))
二义性2:
struct A
{
A(int = );
A(double);
operator int() const;
operator double() const;
};
void f2(long double);
A a;
f2(a);//转换为int还是double?
long lg;
A a2(lg);//A::A(int)还是A::A(double)
由此:
不要令两个类执行相同的类型转换;(只有A转换为B,或者B转换为A)
避免转换目标是内置算术类型的转换。特别是当你已经定义了一个转换为算术类型的转换时,接下来:
不要再定义接受算术类型的重载运算符;
不要再定义转换到多种类型的类型转换。
一言以蔽之:除了显式地向bool类型转换,我们应该尽量避免定义类型转换函数并尽可能限制那些“显然正确”的飞显式构造函数。
重载函数与重载运算符的一个不同点是:
具有相同名字的成员函数和非成员函数不会彼此重载。
而a sym b可能是 a.operatorsym(b)或者operatorsym(a,b)
所以,函数匹配过程中,成员函数和非成员函数都应该考虑在内。
Chapter14:重载运算符的更多相关文章
- c++的重载运算符
c++中允许重载运算符: 这是我辛苦的结果 #include"iostream"using namespace std;class aaa{ int x;public: aaa() ...
- C# 重载运算符
如果你想让自己定义的类型可以用运算符进行运算,那么可以通过重载运算符来实现: 示例: class Salary { public int RMB { get; set; } public static ...
- 【STL】重载运算符
重载运算符 为什么要重载运算符: C++中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如结构体),也需要类似的运算操作.这时就必须在C++中重新定义这些运算符,赋予 ...
- c++中有些重载运算符为什么要返回引用
事实上,我们的重载运算符返回void.返回对象本身.返回对象引用都是可以的,并不是说一定要返回一个引用,只不过在不同的情况下需要不同的返回值. 那么什么情况下要返回对象的引用呢? 原因有两个: 允许进 ...
- C++ Primer : : 第十四章 : 重载运算符与类型转换之类型转换运算符和重载匹配
类型转换运算符 class SmallInt { public: SmallInt(int i = 0) : val(i) { if (i < 0 || i > 255) throw st ...
- C++ Primer : 第十四章 : 重载运算与类型转换之重载运算符
重载前须知 重载运算符是特殊的函数,它们的名字由operator和其后要重载的运算符号共同组成. 因为重载运算符时函数, 因此它包含返回值.参数列表和函数体. 对于重载运算符是成员函数时, 它的第一个 ...
- C#基础知识系列一(goto、i++、三元运算符、ref和out、String和string、重载运算符)
前言 这两天在网上看到的总结很多,尤其是博客园中的,很多很多,也给了我很多的启发,当然自己也总结过,而且有很多人也给与我一些意见和看法.不管怎样,自己还是先把所谓的基础知识加强巩固下吧. 2014年的 ...
- c++重载运算符注意
c++重载运算符的时候加&或不加: 如果加了&表示引用,说明用的都是同一块内存.如果不加,那么用的就是一份拷贝,即不同的内存. 一般连续操作的时候要加&. 可以重新定义一个对象 ...
- C++学习27 用全局函数重载运算符
运算符重载函数既可以声明为类的成员函数,也可以声明为所有类之外的全局函数. 运算符重载函数作为类的成员函数 将运算符重载函数声明为类的成员函数时,二元运算符的参数只有一个,一元运算符不需要参数.之所以 ...
随机推荐
- struts2与struts1整合,Unable to load configuration. - interceptor-ref ... struts.xml
struts.xml中为了与struts1的MVC模式整合,需要类似如下的拦截器的引用 <interceptor-stack name="integration"> & ...
- GLSL基础
GLSL基础 OpenGL Shading Language GLSL作为一种着色语言是纯粹的和GPU打交道的计算机语言.因为GPU是多线程并行处理器,所以GLSL直接面向SIMD模型的多线程计算.G ...
- Data Base sqlServer sa用户登陆失败的解决办法
sqlserver sa用户登陆失败的解决办法 如下图以此模仿: 1.右键-属性 2.找到安全: 3.勾选如图: 4.sa用户密码重置: 5.服务重启:
- leetcode:Plus One
Given a non-negative number represented as an array of digits, plus one to the number. The digits ar ...
- 【笨嘴拙舌WINDOWS】实践检验之剪切板查看器【Delphi】
该程序能够监视Windows剪切板的内容(文字和图片) 其思路是 先调用SetClipBoardViewer(Self.Handle),让Windows剪切板内容发生改变之后,通知本程序: 然后截获W ...
- 物联网操作系统HelloX已成功移植到MinnowBoard MAX开发板上
在HelloX开发团队的努力下,以及Winzent Tech公司(总部在瑞典斯德哥尔摩)的支持下,HelloX最新版本V1.78已成功移植到MinnowBoard MAX开发板上.相关源代码已经发布到 ...
- [转] gc tips(1)
所有应用软件都需要管理内存,一个应用软件的内存管理系统包括了如下准则:什么时候派发内存,要派发多少内存,什么时候把东西放到回收站,以及什么时候清空回收站.MMgc是Flash Player几乎所有内存 ...
- Android 网络流量监听开源项目-ConnectionClass源码分析
很多App要做到极致的话,对网络状态的监听是很有必要的,比如在网络差的时候加载质量一般的小图,缩略图,在网络好的时候,加载高清大图,脸书的android 客户端就是这么做的, 当然伟大的脸书也把这部分 ...
- linux 开机自动启动脚本方法
通过现场对这次天津iptv demo项目的调测.对iptv这套系统有了更好的认识和理解.由于iptv本身需要安装许多服务.而现场实施中有没有把这些需要启动服务的脚本加入到开 机自动运行中.如果服务器重 ...
- 【转】C# 委托的介绍(delegate、Action、Func、predicate)
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递.事件是一种特殊的委托. 1.委托的声明 (1). delegate delegate我们常用到的一种声明 Delegat ...