C++函数参数传递与返回值优化技巧

原创 2016年05月30日 22:04:13

       很久没登陆CSDN,最后一次发帖是换工作的散分贴,之后背井离乡,一去就是八年。八年前的自己对技术充满热情,但是有些井底之蛙,也有些偏激。八年过去,恍然大悟,技术无论大小,总应该有些积累,有些沉淀,有些能让自己和后来之人收益之处,于是乎开始着手写技术文章,文章或许浅显,但或多或少可以与君交流提高。废话那么多,那么第一章就说点简单的吧。

      C++函数的数据传递,这里主要指参数传递与返回值。写这篇的目的主要是总结如何高效的使用函数。

      先来说说函数参数的传递,一个简单的示例:

class param_obj{

};
class return_obj{

};

return_obj func(param_obj);

        这里的参数传递采用的是按值传递

        如果读过《effective c++》,或许记得书中第二十条,使用常量引用(pass-by-reference-to-const)来代替按值传递(pass-by-value)。

很明显传递引用省去了拷贝构造函数的执行,对于复杂对象例如其包含指向堆的指针或者是多个基类的子类,那么省下的不止一个函数的执行。关于其他理由,可直接看第二十条描述,这里不做赘述。

      说下常量引用,或许很多人包括我自己曾喜欢直接传递非常量引用。这里按值传递,本身就不希望通过该变量带回数据(也不推荐),所以不给函数参数对象的修改权限是合情合理的。那么有一点值得注意的就是,该对象的get方法,甚至那些计算但不修改对象的方法都应加常量限定符const。常量对象只能调用常量方法

     于是示例代码如下:

class param_obj{
    public:
        int getValue(); const
        int valueSquare(); const 
     private:
        int value;
};
class return_obj{

};

return_obj func(const param_obj&);

       习惯使用常量限定符来描述那些不改变类的方法是有很多好处的,且也最符合设计逻辑


       接下来看函数返回值。

       没错,《effective c++》第21条谈到了返回值,主要谈到不要为了效率而返回引用或者指针等,具体请自行查阅,这里不详述。

       这里笔者主要想提两种方式

       第一是利用RVO(return value optimization),返回值优化。看如下的例子。

#include<iostream>
using namespace std;
class Data{
public:
  Data(){};
  Data(const Data &data){
    cout<<"copy constructor"<<endl;
  };
  Data &operator=(const Data&){
    cout<<"assignment operator"<<endl;
    return *this;
  };
};


Data func(){
  Data f;
  cout<<"f address:"<<&f<<endl;
  return f;
}


int main(void){
  Data a;
  a = func();
  cout<<"a address:"<<&a<<endl;
  Data b = func();
  cout<<"b address:"<<&b<<endl;
  return 0;
}

g++ 4.8 编译执行结果如下

f address:0x7fff2018d72f
assignment operator
a address:0x7fff2018d72e
f address:0x7fff2018d72d
b address:0x7fff2018d72d


      一般函数返回对象时会调用copy构造函数,而这里可以看出两次返回都没有调用copy构造函数,所以这里都进行了RVO。当然此处因为返回的是命名对象,使用的又称为NRVO。

        再看对象b的例子,是不是惊奇的发现赋值操作也被优化掉了,对象b的指针和函数内对象的指针一致。这里我们是否可以得出一条结论:

        在一个变量得到它所需值的时候定义它

        这也正是《effective c++》第26条所强调的,不过那条的描述并不关注这里的优化。

        对于RVO,很多编译器是默认打开,特别是gnu的编译器,所以广大码农可能无意识就享受到了RVO。当然我们能做到的更好的一步就是在赋值函数返回对象时去定义那个对象。

        是不是发现曾经往往以为的要返回指针或是引用才是高效的在这些编译优化中迎刃而解呢。可以仔细阅读自己项目中,是否有很多为了效率而丢失的便利性的写法,例如输出参数是vector时,在外部定义,再通过引用传递给函数这种

vector<int> things;
getThings(things);

void getThings(vector<int> &things){
...
}
        可以大胆的改为

vector<int> things = getThings();

vector<int> getThings(){
    vector<int> things;
    ...
    return things;
}

        这种写法的好处是很多的,调用处更简洁,返回的vector也可以直接嵌套于for语句或作为其他函数输入。


        除了RVO,另一种可以利用的是右值引用。这个是C++11的特性。需打开编译开关。

        如下示例

#include<iostream>
using namespace std;
class Data{
public:
  Data(){};
  Data(const Data &data){
    cout<<"copy constructor"<<endl;
  };
  Data &operator=(const Data&){
    cout<<"assignment operator"<<endl;
    return *this;
  };
  Data &operator=(Data &&){
    cout<<"rvalue assignment operator"<<endl;
    return *this;
  };
};

Data func(){
  Data f;
  return f;
}

int main(void){
  Data a;
  a = func();
  Data b;
  b = a;
  return 0;
}
      输出

rvalue assignment operator
assignment operator

        可以看出, 因为赋值符号右边是函数返回的局部对象,调用了传递右值的赋值操作。右值一般后面不会再被引用,所以这里可做的优化便是右值中的变量可以直接转移,包括指针,无需新申请内存。关于更具体的左右值概念,可以查看网上资料。这里不做详述。

        了解RVO 与 右值引用在很多情况下可以写出更合理与高效的编码。 当然根据项目实际情况,返回指针或是引用也会出现,当然相信仔细琢磨也能找出更优雅的解决方案。


       做个小结:

       关于参数传递:

             使用常引用代替值传递

             学会用const限定方法。

       关于返回值:

             RVO与NRVO解决了临时对象的copy

             学会在函数返回赋值时定义对象

             右值引用能有效改善赋值的效率,特别有指向堆的内容时






    

版权声明:本文为博主原创文章,未经博主允许不得转载。

C++ - 函数的参数传递方式和返回值

一、参数传递方式 1、普通参数 int AbOne(int a, int b) { return a+b; } 2、引用参数 int AbTwo(int &a, int &b) ...
  • linycobie
  • linycobie
  • 2012年03月07日 19:59
  • 239

函数返回值与参数传递

对结构体返回有些疑问,我在函数内部定义了一个结构体并打印出他的地址,又在main函数中定义一个结构体等于函数返回再打印地址,得到的结果竟然是一样的。今天看到一篇很好的博文,记录如下。原博文地址:htt...
  • Android_chunhui
  • Android_chunhui
  • 2016年10月24日 09:28
  • 265

C++函数参数和返回值三种传递方式

C++函数参数和返回值三种传递方式:值传递、指针传递和引用传递 (着重理解) 引用与指针的比较 引用是 C++中的概念,初学者容易把引用和指针混淆一起。一下程序中,n 是m 的 一个引用(r...
  • langonghan
  • langonghan
  • 2011年02月21日 11:11
  • 4708

C函数参数传递与返回值传递

(1)参数传递        __stdcall和__cdecl都是函数调用约定关键字,先给出这两者的区别,然后举实例分析:   __stdcall:参数由右向左压入堆栈;堆栈由函数本身清理。 ...
  • qq_33921804
  • qq_33921804
  • 2016年09月25日 17:54
  • 849

C++函数参数传递和返回值

1、函数的参数传递1.1函数调用的机制   函数的调用过程实际是对栈空间的操作过程(先进后出)。因为调用函数是用栈空间来保存信息的。函数调用过程大致描述如下:1)建立被调用函数的栈空间;2)保存调用函...
  • tangtj
  • tangtj
  • 2008年12月16日 20:05
  • 720

1.4参数传递与返回值(1)

继续分析complex类 const member functions(常量成员函数)   上图类中的函数:     double real () const {return re;}     dou...
  • zyq11223
  • zyq11223
  • 2017年05月11日 17:12
  • 143

第二周 项目一 -C++语言中函数参数传递的三种方式

/* *Copyright (c) 2017,烟台大学计算机与控制工程学院 *All rights reserved. *文件名称: *作 者:孙仁圆 *完成日期:2017年9月3日 *版 本 ...
  • renyuansun
  • renyuansun
  • 2017年09月03日 22:30
  • 193

c++ 用指针将函数作为参数传参

今天发现了一种写法,顺便记录一下。废话不多说,先看代码: #include using namespace std; int addition(int x, int y){ retur...
  • sinolzeng
  • sinolzeng
  • 2017年06月09日 10:06
  • 481

参数和返回值传递总结

函数的传参方式有值传递和地址传递,地址传递又分为指针传递和引用传递。 函数的返回值也有值传递和地址传递,地址传递又分为指针传递和引用传递。 参数的值传递:进入函数体后,为形参分配地址空间,将实参赋...
  • u012420933
  • u012420933
  • 2013年10月27日 13:49
  • 748

C++中数组的基础知识

》数组            数组(Array)是由固定数目元素组成的数据结构,同一数组的所有元素的类型都相同。数组元素是通过下标进行访问的。数组可以是一维的,也可以是多维的,许多重要应用的数据结构...
  • darkxiaoming
  • darkxiaoming
  • 2017年04月17日 12:35
  • 479
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++函数参数传递与返回值优化技巧
举报原因:
原因补充:

(最多只允许输入30个字)