zoukankan      html  css  js  c++  java
  • 让函数的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。

  • 相关阅读:
    学习游戏设计
    AspectJ
    Spring AOP进行日志记录,管理 (使用Spring的拦截器功能获取对action中每个方法的调用情况,在方法调用前和调用后记录相关日志。)
    Java内存泄露测试及工具
    使用Tomcat插件开发WEB应用
    想学习建个网站?WAMP Server助你在Windows上快速搭建PHP集成环境
    UML 基础:类图
    Impala学习--Impala前端代码分析,Impala后端代码分析
    Impala学习--Impala概述,Impala系统架构
    图论--2-SAT--HDU/HDOJ 1814 Peaceful Commission
  • 原文地址:https://www.cnblogs.com/yunlambert/p/10061325.html
Copyright © 2011-2022 走看看