让函数的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 ...
随机推荐
- ffmpeg/ffplay源码剖析笔记<转>
转载:http://www.cnblogs.com/azraelly/ http://www.cnblogs.com/azraelly/archive/2013/01/18/2865858.html ...
- FFmpeg for Android compiled with x264, libass, fontconfig, freetype and fribidi
android下打算使用ffmpeg的 drawtext ,不过需要 --enable-libfreetype 但是freetype是个第三方库,所以需要先编译freetype,然后再编译ffmpe ...
- 用SSIS包导入数据
创建一个简单ETL包.打开 Step 1:创建新的Integration Services项目 在开始菜单中找到SQL Server Data Tools并打开,在Microsoft SQL Serv ...
- TCP/IP 笔记 7 Ping
lenovo-myc@lenovomyc-Lenovo-Product:~$ ping www.baidu.com PING www.a.shifen.com (() bytes of data. 这 ...
- MSSQL 日期查询 包含NULL值
方一: 以下做法保证数据里面没有NULL值 '') '') '') '') exec sp_executesql N'select ide_code as ''系统编号'',name as ''申请专 ...
- JS继承方式详解
js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念.所以,要想实现 ...
- ActionBarActivity的使用注意事项
1.调用getActionbar()方法返回为空的解决方法 此activity是设计来支持低版本系统用actionbar的,低版本没有getActionbar() 需要使用 getSupportAct ...
- go语言linux下安装
1.从http://golang.org/dl/下载最新版本的GO语言二进制档案包. 注意:根据操作系统和计算架构正确选择档案包 2.使用tar命令将档案包解压到/usr/local目录中.具体方法如 ...
- 04 UUID
1 什么是UUID UUID 的目的是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定. 2 应用场景 MySQL数据库不能想oracle数据库那样创建序列,就 ...
- js实现上传单个文件
js上传文件:js 上传单个文件(任意大小) 疯狂代码 http://www.CrazyCoder.cn/ :http:/www.CrazyCoder.cn/Javascript/Article832 ...