庖丁解牛,庖丁指的是编译器,而我们则是提供牛的人,牛就是被重载的操作。当编译器遇到被重载的操作,能够准确地找出合适的一个,犹如庖丁解牛,游刃有余。

重载主要分为函数重载和操作符重载。函数重载应该大家都会稍微熟悉一些,操作符可能略显生疏。但是不管怎样,我们讨论完后肯定会有更深刻的理解。

一、函数重载(成员或非成员)

具有相同名字而形参表不同的多个函数形成了函数的重载。

注意:仅返回值不同是不能形成函数重载的。首先,因为调用函数时还尚不清楚它的返回值究竟是什么类型(这是书本的说法);但这只是表象,其实只有返回值不同的多个函数根本就通不过编译,何来调用一说。看过第二式的同学都知道,编译器在处理函数名时,有一个“mangling”操作,而该操作是以函数名和各个参数的类型为基础的(其中并不包括返回值),所以说只有返回值不同的多个函数经过编译器的处理后依然同名(而参数个数或类型不同则不会同名)。为什么C++可以支持函数重载,就是因为它有函数名的“mangling”操作,通过该操作后,符合重载要求的函数就会获得不同的函数名,而函数名可以得到函数地址,通过函数地址来调用相应的函数。如果有两个相同的函数名,那么编译器同学根本无法确定你要调用的到底是哪个函数,这时候它只有很不开心地提示一个错误了。

还有一个在使用函数时遇到的问题就是,第一个有默认值的参数后面所有的参数必须都有默认值。这主要是为了避免参数传递的二义性。如果符合上述条件,那编译器只需按照参数的声明顺序传递实参就行了;如果不符合该条件编译器君可能就会有小脾气了。例如下面的情况:

voidfunc (int a, int b = 0, int c)

如果调用是这样:func(1,2);参数是传给a,b还是a,c?如果每次都提供3个参数那中间的默认值还有什么意义?你可能说肯定是传给a,c因为传给a,b的话,那c的实参不就没了吗。有默认值又不是不能传参数,谁知道你是不是真的少传了一个参数。编译器君比较单纯,这个复杂的社会它暂时还是无法适应,所以大家还是体谅一下吧。

-----------------------------------------------------------------

在进入操作符重载的具体情况之前,先来个“学前培训”。

操作符的重载主要是为了给我们自定义的类提供方便的操作。大部分操作符可以重载,不能重载的操作符有如下四个:

::      .*      .      ?:

重载操作符的基本语法如下:

返回值类型  operator操作符(参数列表)

需要注意事项如下:

1.  不能创建任何新的操作符

2.  用于内置类型的操作符,其含义不能改变,也不能增加额外的新操作

3.  操作符的优先级、结合性和操作数数目不能改变

4.  除了函数调用操作符operator()之外,重载操作符时使用默认实参是非法的。

5.  经过重载的操作符将失去短路求值特性

6.  若为成员函数,则其隐含的this形参限定为第一个操作数;非类成员而操作类实例时需为友元关系

二、算数运算符重载(包括自增/自减)

算数操作符比较多,我就选几个基础的为大家抛砖引玉一下(∩_∩)

例如有如下类:

class A
{
public:int a;A& operator+(A& right);
};

操作符函数定义如下:

A& A::operator +(A& right)
{this->a += right.a;return *this;
}

则可如下调用:

A a1,a2;//定义A的实例对象
a1.a = 1;
a2.a = 2;
a1 = a1 + a2;//a1调用operator+函数并将a2作为参数

后置++操作符的定义(假设已在类A中声明):

A  A::operator ++(int)//int形参主要是为了和前自增相区别
{A temp(*this);++this->a;return temp;
}//后置时应返回旧值

调用:

A a1;
a1.a = 1;
a1++;

前置++操作符的定义(假设已在类A中声明):

A& A::operator ++()
{this->a++;return *this;
}//为了与内置类型一致,返回该类类型的引用

调用:

A a1;
a1.a = 1;
++a1;

三、输入/输出操作符重载

为了与IO标准库一致,操作符应该接受istream/ostream的引用作为第一个形参,对类类型const对象的引用作为第二个形参,并返回对应形参的引用。

接着用上面的类,定义如下函数:

istream& operator>>(istream& in, A& one)
{in>>one.a;return in;
}

可如下调用:

A a1;
cin>>a1;

输出操作符定义如下:

ostream& operator<<(ostream& out, A& one)
{out<<one.a<<endl;return out;
}

可如下调用:

cout<<a1;

重载输入/输出操作符后就可以在输入/输出时自定义相应的操作,用起来真的很方便呢。

四、强制类型转换操作符重载

该重载函数没有返回值(其实已经隐含在函数名中),没有形参,形式如下:

operatortype();

只要type是一个类型,包括基本数据类型,我们定义的类或者结构体都可以进行相应的转换。

接着用上面的类定义,假设已声明该成员函数,定义如下:

A::operator int()
{return this->a;
}

可进行如下的调用:

A a1;
a1.a = 6;
int i = (int)a1;//调用我们定义的类型转换函数

当然也可以定义其它类型的转换,如果你喜欢的话,但是我们就不一一展示了哟。

五、下标操作符重载

首先,下标操作符必须定义为类成员函数;其次,类定义下标操作符时,一般需要定义两个版本(可以由const或非const对象来调用)。一个为非const成员并返回引用,另一个为const成员并返回const引用。

我做如下类定义:

class A
{
public:vector<int> data;int& operator[](const size_t);const int& operator[](const size_t)const;
};

实现相应的操作符函数:

int& A::operator[](const size_t index)
{return data[index];
}
const int& A::operator[](const size_t index)const
{return data[index];
}

则可实现如下调用:

A a1;
a1.data.push_back(1);
a1.data.push_back(2);
a1.data.push_back(3);
cout<<a1[1]<<endl;//这里调用了重载操作符(非const版本)

六、函数调用操作符重载

函数调用操作符也可以用于重载(即括号操作符),且必须声明为成员函数。一个类可以定义多个版本的函数调用操作符,由形参的个数或类型加以区别。

定义了调用操作符的类,其对象成为函数对象,在STL中有较多的应用。

接着用上面定义的类,添加成员函数,定义如下:

void A::operator()()//第一个括号是被重载的操作符,第二个是形参列表
{cout<<"Hello world!"<<endl;
}//简单地输出信息

可以这样调用:

A()();//第一个括号产生对象,第二个是调用重载的操作符函数

或者:

A  a;
a();

还是挺好玩的吧(*^-^*)

最后,有些操作符是必须定义为成员函数的,有些则不然。那对于那些可以定义为非成员函数的情况,我们如何确定何时定义为成员呢?我个人还是倾向于不管三七二十一把它定义为成员函数,因为通常的操作都会涉及类中的成员变量,封装性好的类定义是不会允许外面的函数随意访问成员变量的。当然只要符合使用习惯,即使定义为非成员函数也是无伤大雅的。

还有一种是不能定义为成员的情况,这种情况是使用友元的典型场景:我们无法得到操作符第一形参的类定义(因为成员函数的this指针将绑定到第一个形参),而又需要通过它来操作我们的类成员(例如输入/输出操作符)。

C++独孤九剑第七式——庖丁解牛(各种重载操作)相关推荐

  1. 独孤九剑第七式-物以类聚 人以群分(K-means模型)

  2. PS素材挖掘七式(留学 个人陈述 personal statement)

    http://goabroad.xdf.cn/201605/10511032.html PS素材挖掘七式之一:从遇见最好的自己开始 讲真,小编做指导文书写作这份工,最害怕的要求就是提供优秀PS范文.这 ...

  3. 视易精通收银服务器自动关机,视易精通量贩式收银系统操作-手册3.0.doc

    视易精通量贩式收银系统操作-手册3.0 ~ ~~~ 视易精通 收银管理系统 {用户操作手册} 目录 TOC \o "1-3" \h \u HYPERLINK \l _Toc2376 ...

  4. golang常用库之-mgo.v2包、MongoDB官方go-mongo-driver包、七牛Qmgo包 | go操作mongodb、mongodb bson

    文章目录 golang常用库之-mgo.v2包.MongoDB官方go-mongo-driver包.七牛Qmgo包 | go操作mongodb.mongodb bson 一.[不推荐]mgo.v2包 ...

  5. 刁肥宅手笔:纯C语言实现链式队列的相关操作

    先上图,以图服人: 图一 程序运行截图1 图二 程序运行截图2 上代码: 头文件LinkQueue.h: /*LinkQueue.h*/#ifndef LINKQUEUE_H_INCLUDED #de ...

  6. SEOER应当避免赌徒式的外链操作手法

    什么是赌徒式的外链操作手法 这个概念听起来很玄乎,其实不难,所谓赌徒式的外链操作手法也就是把鸡蛋放心一个篮子,或者说让一只鸡生蛋.之所以我称为赌徒式的,因为这是一个很有意思的现象,就是很多朋友在发外链 ...

  7. 实验七 索引和视图的操作Mysql索引和视图

    实验七 索引和视图的操作 [实验目的]:①学会使用update.delete.insert命令 ②掌握视图的建立.修改.更新和删除. ③掌握索引的创建和使用. [实验内容]:相关命令写在作业本上. 1 ...

  8. 【三年面试五年模拟】算法工程师的独孤九剑秘籍(第七式)

    写在前面 [三年面试五年模拟]栏目专注于分享CV算法与机器学习相关的经典&&必备&&高价值的面试知识点,并向着更实战,更真实,更从容的方向不断优化迭代.也欢迎大家提出宝 ...

  9. 初入c++(七)运算符的重载+、-、*、/、[]、自加++的重载

    1.为什么要用到运算符的重载 所谓重载,就是赋予新的含义.函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作.运算符重载(Operator Ov ...

最新文章

  1. 软件工程实训有必要吗_人工智能专业值得读吗?就业如何?
  2. 新春快乐!数据派又双叒叕送福利啦~
  3. 【杂谈】为什么你在有三AI看不到最新论文的解读,其实一直都有的
  4. mysql字符串语法_MySQL语法模板 函数:字符串
  5. 性能测试和性能分析的基础概念
  6. C#中全角转半角以及半角转全角
  7. 项目管理的几个概念(WBS、OBS、RBS、BOM、CWS、CA)总结与区分
  8. 《Linux命令行大全》:1-6:重定向和管道(很精彩)
  9. 杭电算法题 HDU 1000-1004
  10. 软件测试之常见性能测试流程
  11. Pyinstaller打包过程中报错“AttributeError: module 'enum' has no attribute 'IntFlag'”问题解决
  12. 模糊自适应整定PID控制
  13. 适配 Android N 需要注意什么
  14. 网络技术大讲堂:什么是IPv6+?
  15. 这些年,亲眼所见的软件公司中混迹的老油条汇总
  16. 垂直领域知识图谱_垂直知识图谱的构建与应用研究
  17. 无限卡,无限流量,无限滚动!
  18. 计算机系徽 节徽设计,数学节节徽设计图片
  19. 关于电商商品数据API接口列表,你想知道的(详情页、Sku信息、商品描述、评论问答列表)
  20. [项目实战] 云知梦Laravel5.4电商实战项目VIP视频教程

热门文章

  1. 深入学习“主动学习”:如何显著地减少标注代价
  2. mysql 创建索引语句
  3. STATA 图片编辑器导出为PDF格式 中文乱码
  4. 了解目前市面上几款常见的四足机器人
  5. html中input的color,怎么使用html5中input的color颜色拾取器?使用方法分享!
  6. idea导入导出sql文件
  7. java读取欧姆龙plc,欧姆龙CJ2M系列PLC与PLC之间的数据互相读取设定
  8. 美国独立服务器好吗?
  9. LeetCode-6:ZigZag Conversion(Z字形变换)
  10. Android 四大组件基本介绍