让函数的input、output更"函数化"
前言
我们都知道函数的基本形式为:output f(input),且先按这种形式进行input与output的分析,我们的input与output可以有更好的设计方式,而我们的output是选择使用return by value还是return by reference也会有一定的思考,使得我们的函数更加"函数化"。
input
函数的input一般指通过传参给函数,这些参数一般是通过传递引用到const参数(const T&)来表示(当然基本类型直接传value也是ok的)。我们在函数里可以调用这个参数,但同时因为这个参数是const类型,所以不能进行修改,所以说const T&的input在函数中的作用只是一个“只读”的参数。
如果我们想让传入的参数同时还有output的能力,这样表达可能有些奇怪,换一句话说就是我们希望我们传入的同时具有被修改的能力,这样在该函数中修改后我们甚至都不用返回这个input的新值,就能在其他函数中调用该参数的最新的值,这里只需要使用T &就行了。
output
再回到我们的函数基本形式上来:Output f(const Input& input),Output是函数的返回类型;但正如我们前面所说的,如果我们想让参数同时具有output的能力,或许我们可以这样写:
void f(const Input& input, Output& output);
这样,我们的output参数在函数中被修改,自然也就不用再返回Output了,这样不是非常的好么?
先说一下这个新方法(return by reference)的一些弊端:
1.不太符合语言的自然性
输出参数之所以是output,就因为它是输出(output类型)的呀.......假如我们就按刚才提到的方法做,我们看看我们在其他函数中应该怎么写:
Output output;
.......
f(input,output);
如果我们再加上几个output:
Output output1,output2,output3,output4;
f(input,output1);
f(input,output2);
f(input,output3);
g(input,output4);
相比我们如果用Output f(const Input& input)来表达:
Output output = f(input);
return by reference显得不自然,如果出现在工程项目里,一句f(input,output)可能都会让你忘记这个函数是想干什么,这个output又是什么东西;所以第一点,这个语法不够自然。
2.我们并不能确保我们的函数是真的将output赋予新值了
我们通过return by value还是可以较为方便的通过检测机制看到这个值是否发生变化的,但是如果是return by reference,我们实际上并不能确切的得知output是否确实发生改变了(在函数中再加入一个检测机制并不是一个好方法.......)
虽然利用return来返回类型确实不是被很多人使用,但是除非是真的无法用return来返回output,我一般都会建议利用return来返回,下面说说使用Output f(const Input& input)的好处:
性能
一部分人可能会认为通过return返回,需要拷贝赋值等等会耗费空间的举措,但是C++中已经有了很多语言机制去消除returning by value的copy,首要提及的自然是RVO(Return Value Optimisation):在这里举个例子吧,比如下面这个程序:
T f()
{
....
return T(constructor arguments);
}
T t=f();
理论上,会创建三个T对象,第一个是在T f()的return中的构造对象,第二个是拷贝了上一个对象的临时对象,第三个则是将临时对象copy给t。但是实际上,RVO让我们的编译器移除了第一个与第二个对象,允许我们直接使用传递到f中的构造函数的参数来初始化t。有兴趣的可以参考一下这篇[博客(https://www.cnblogs.com/xkfz007/archive/2012/07/21/2602110.html)]。当然我们也有std::move移动构造,比如我们返回一个STL的容器的时候并不是拷贝而是会移动它,由于优化,与拷贝所耗费时间都是差不多的。
差错处理
正如我们关于return by reference的第二个缺陷,我们在return by value会有很好的办法去处理他:
bool f(const Input& input, Output& output);
Output output;
bool success = f(input, output);
if (success)
{
// use output ...
}
通过返回类型我们可以很好的知道是否执行成功,在网络编程中,我i们的基本函数bind()、connect()、listen()、socket()等等都是通过这种返回形式来执行差错处理的。
而在C++17标准中有了一个std::optional的方法,如果函数执行成功我们将得到正确的output,如果没有我们将返回一个空的值(empty),更方便我们的差错处理。
返回多个类型
想一想如果我们利用return by reference返回多个类型,该怎么写?
Output1 output1;
Output2 output2;
void f(const Input& intput, Output1& output1, Output2& output2);
当然我们也可以定义一个结构体,将所有数据存进来再通过return by reference返回:
struct Outputs
{
Output1 output1;
Output2 output2;
};
Outputs f(const Input& input);
但是如果我们return by value,就可以通过tuple来实现:
std::tuple<Output1, Output2> f(const Input& input);
Output1 output1;
Output2 output2;
std::tie(output1, output2) = f(inputs);
而在C++标准中我们可以通过auto直接返回多个参数:
auto [output1, output2] = f(const Input& input);
支持了这种结构化的绑定,会更加方便。
结论
或许用"该选择return by value还是return by reference"可能更好一些。自己的结论就是不到无法使用return by value的时候,出于对代码的清晰性和表达性的考量,尽量别使用return by reference。
让函数的input、output更"函数化"的更多相关文章
- PHP输出缓冲控制- Output Control 函数应用详解
说到输出缓冲,首先要说的是一个叫做缓冲器(buffer)的东西.举个简单的例子说明他的作用:我们在编辑一篇文档时,在我们没有保存之前,系统是不会向磁盘写入的,而是写到buffer中,当buffer写满 ...
- EPANET中读取INPUT文件的函数文件——INPUT1.C/INPUT2.C/INPUT3.C
首先介绍下这3个文件的关系:可以说INPUT1.C的函数粒度最大,它的函数getdata()就完成了整个INPUT文件数据的读入,该函数又调用了INPUT2.C中的部分函数,INPUT2.C文件中的函 ...
- 深入理解javascript函数进阶系列第二篇——函数柯里化
前面的话 函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名.本文将详细 ...
- 实验与作业(Python)-02 Python函数入门与温度转换程序(函数、input、eval、int、float、列表)
截止日期 实验目标 学会定义函数,使用函数.学会导入在某个文件中定义的函数. input获得值,然后通过eval或者int.float将其转换为相应的类型. 学会使用列表:访问列表.append.遍历 ...
- 学好Spark/Kafka必须要掌握的Scala技术点(三)高阶函数、方法、柯里化、隐式转换
5. 高阶函数 Scala中的高阶函数包含:作为值的函数.匿名函数.闭包.柯里化等,可以把函数作为参数传递给方法或函数. 5.1 作为值的函数 定义函数时格式: val 变量名 = (输入参数类型和个 ...
- input()和print()函数同时输入输出多个数据--python3
使用input()和print()函数同时输入输出多个数据,需要空格分割输入信息 #!/usr/bin/python3#-*- conding:utf-8 -*- name, age, QQ = in ...
- 使用QFileInfo类获取文件信息(在NTFS文件系统上,出于性能考虑,文件的所有权和权限检查在默认情况下是被禁用的,通过qt_ntfs_permission_lookup开启和操作。absolutePath()必须查询文件系统。而path()函数,可以直接作用于文件名本身,所以,path() 函数的运行会更快)
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Amnes1a/article/details/65444966QFileInfo类为我们提供了系统无 ...
- PHP7函数大全(4553个函数)
转载来自: http://www.infocool.net/kb/PHP/201607/168683.html a 函数 说明 abs 绝对值 acos 反余弦 acosh 反双曲余弦 addcsla ...
- 文成小盆友python-num3 集合,函数,-- 部分内置函数
本接主要内容: set -- 集合数据类型 函数 自定义函数 部分内置函数 一.set 集合数据类型 set集合,是一个无序且不重复的元素集合 集合基本特性 无序 不重复 创建集合 #!/bin/en ...
随机推荐
- mysql:mysql Access denied for user root@
最近用本地Navicat连接集群的mysql,报了上述的错误,我认为是权限问题 之前试过赋权限给所有人,但是我这边还是连接不上,无奈,试试只分给我一个IP 开始:mysql -uroot -p //先 ...
- MyBatis配置Setting详细说明
该表格转载自http://blog.csdn.net/summer_yuxia/article/details/53169227 setting是指定MyBatis的一些全局配置属性,这是MyBati ...
- ???Spring集成MyBatis02 【不推荐使用,了解即可】
2017年5月19日09:31:22 由于该种方法比较麻烦,所以三少暂时不更新,哈哈哈:待更新...
- 业务逻辑:五、完成认证用户的动态授权功能 六、完成Shiro整合Ehcache缓存权限数据
一. 完成认证用户的动态授权功能 提示:根据当前认证用户查询数据库,获取其对应的权限,为其授权 操作步骤: 在realm的授权方法中通过使用principals对象获取到当前登录用户 创建一个授权信息 ...
- DR客户端一直连接服务器....(6)
DR客户端一直连接服务器....(非未选择网卡)解决方法: 控制面板-->网络和 Internet-->网络连接,打开本地连接(以太网)属性,将IPV4协议前面的对勾去掉,重启DR 这样D ...
- 算法Sedgewick第四版-第1章基础-004一封装交易对象
1. package ADT; /****************************************************************************** * Co ...
- Java-马士兵设计模式学习笔记-工厂模式-模拟Spring读取Properties文件
一.目标:读取properties文件,获得类名来生成对象 二.类 1.Movable.java public interface Movable { void run(); } 2.Car.java ...
- Java-马士兵设计模式学习笔记-工厂模式-单例及多例
一.单例的作用是用于控制类的生成方式,而不让外部类任意new对象 1.Car.java import java.util.ArrayList; import java.util.List; publi ...
- Linux kdb命令
一.简介 Linux 内核调试器(KDB)允许您调试 Linux 内核.这个恰如其名的工具实质上是内核代码的补丁,它允许高手访问内核内存和数据结构.KDB 的主要优点之一就是它不需要用另一台机器进行调 ...
- Entity Framework Tutorial Basics(9):Entity Relationships
这篇前面已经转载出来了,地址:http://www.cnblogs.com/purplefox2008/p/5646466.html