引用参数与引用返回值
来源:互联网 发布:网络写手签约哪个网站 编辑:程序博客网 时间:2024/06/03 20:19
转载:
http://www.cnblogs.com/bigshow/archive/2008/11/10/1330514.html
经常看到这样的声明:T& func(T& t),这种声明和T func(T t)有什么区别?书上的解释是为了提高效率,究竟是如何提高效率的呢?内部执行了什么操作?本文通过8个小例子对引用参数和引用返回进行了一次彻底的排查。
首先看一下在类的成员函数中的引用参数和引用返回值:
<pre name="code" class="cpp">#include <iostream>using namespace std;class A{public: int x; A(){}//构造函数 A(const A& other)//拷贝构造函数 { this->x = other.x; cout << "Copy" << endl; } ~A(){}//析构函数 A& operator=(const A& other)//赋值函数 { this->x = other.x; cout << "Assign" << endl; return *this; } void func1(A a) { } void func2(A& a) { } A func3() { return *this; } A& func4() { return *this; }};A& func5(A& a){ return a;}A& func6(A a){ return a;}A func7(A& a){ return a;}A func8(A a){ return a;}int main(){ /*A a1, a2; a2.func1(a1);*/ /*A a1, a2; a2.func2(a1);*/ /*A a1, a2; a2 = a1.func3();*/ /*A a1, a2; a2 = a1.func4();*/ /*A a1; a1.x = 1; A& a2 = func5(a1); a1.x++; cout << a1.x << endl; cout << a2.x << endl;*/ /*A a1; a1.x = 1; A& a2 = func6(a1); a1.x++; cout << a1.x << endl; cout << a2.x << endl;*/ /*A a1; a1.x = 1; const A& a2 = func7(a1); a1.x++; cout << a1.x << endl; cout << a2.x << endl;*/ /*A a1; a1.x = 1; const A& a2 = func8(a1); a1.x++; cout << a1.x << endl; cout << a2.x << endl;*/ A a1; a1.x = 1; const A a2 = func5(a1);//对象 a1.x++; cout << a1.x << endl; cout << a2.x << endl; return 0;}/*---------fun1--------------$ g++ -o test1 test1.cpp$ ./test1 Copy---------fun2--------------$ g++ -o test1 test1.cpp$ ./test1---------fun3--------------$ g++ -o test1 test1.cpp$ ./test1 CopyAssign---------fun4--------------$ g++ -o test1 test1.cpp$ ./test1 Assign---------fun5--------------$ g++ -o test1 test1.cpp$ ./test1 22---------fun6--------------$ g++ -o test1 test1.cpptest1.cpp: In member function ‘A& A::func6(A)’:test1.cpp:54:10: warning: reference to local variable ‘a’ returned [-Wreturn-local-addr] A& func6(A a) ^$ ./test1 Copy21---------fun7--------------$ g++ -o test1 test1.cpp$ ./test1 Copy21---------fun8--------------$ g++ -o test1 test1.cpp$ ./test1 CopyCopy21$ g++ -o test1 test1.cpp$ ./test1 Copy21*/
这个类很简单,只有一个成员变量x,并且定义了默认构造函数、拷贝构造函数、析构函数和赋值函数。为了能够更清楚地看到哪个拷贝构造函数与赋值函数是否被调用,在这两个函数中添加了一些输出信息。
类中还定义了四个成员函数,下面分别分析这四个函数的执行情况。
(1) 在main()函数中调用func1():
---------fun1--------------
$ g++ -o test1 test1.cpp
$ ./test1
Copy
为什么会有这样的输出结果呢?这是由于func1()中传递的是值参数,因此在执行函数体之前会先产生一个临时对象,然后调用类的拷贝构造函数初始化这个临时对象,从而输出了"Copy"。在函数内部操作的是这个临时对象,对临时对象所做的任何修改不会反映到函数的实参上。
(2) 在main()函数中调用func2()以与func1()对比:
---------fun2--------------
$ g++ -o test1 test1.cpp
$ ./test1
结果什么也没有输出。
这是由于传入的是一个引用参数,因此在函数内部不需要产生一个临时对象来保存对象信息,因此不会调用拷贝构造函数。这就是引用参数的作用,减少一次对象的拷贝,提高了函数的效率。
(3) 在main()函数中调用func3():
---------fun3--------------
$ g++ -o test1 test1.cpp
$ ./test1
Copy
Assign
为什么会输出"Copy"呢?这是因为函数采用的是值返回,因此为了保存返回值,需要先创建一个临时对象,然后调用类的拷贝构造函数将*this的内容拷贝到这个临时对象中,再将临时对象返回。最后通过赋值函数将该临时对象的内容赋值给新对象。
(4) 在main()函数中调用func4()以与func3()对比:
---------fun4--------------
$ g++ -o test1 test1.cpp
$ ./test1
Assign
只调用了赋值函数,这是引用函数采用的是引用返回,因此直接返回对象自身的引用*this,不需要创建临时对象来保存对象信息,因此不会调用拷贝构造函数。最后通过赋值函数直接将对象本身的内容赋值给新对象。这就是引用返回值的作用,减少了一次对象的拷贝,提高了函数的效率。
总结一下:在类的成员函数中,使用引用参数和引用返回值都不需要产生临时对象,减少了一次对象的拷贝,提高了函数的效率。
那么,如果将参数作为返回值返回,并且用引用接收返回值将会产生什么效果呢?下面定义四个全局函数:A& func5(A& a)
{
return a;
}
A& func6(A a)
{
return a;
}
A func7(A& a)
{
return a;
}
A func8(A a)
{
return a;
}
(5) 在main()函数中调用func5():
---------fun5--------------
$ g++ -o test1 test1.cpp
$ ./test1
2
2
func5()采用了引用参数,并且以引用返回值的方式返回了该参数,因此a2是a1的一个引用,对a1的任何改变都会反映到a2上,所以a1、a2的成员变量x的值相同。
编译的时候会报一个警告:
---------fun6--------------
$ g++ -o test1 test1.cpp
test1.cpp: In member function ‘A& A::func6(A)’:
test1.cpp:54:10: warning: reference to local variable ‘a’ returned [-Wreturn-local-addr]
A& func6(A a)
^
$ ./test1
Copy
2
1
警告的意思就是返回了一个局部变量的引用,这种用法实际上是错误的。局部变量在函数返回前就会被释放,因此实际上a2引用到的一块不可知的内存,这从输出的a2.x的值"4198610"也可以看出来。至于输出"Copy",是因为采用的是值参数,上面已经讨论过,这里不再赘述。
(7) 在main()函数中调用func7():
---------fun7--------------$ g++ -o test1 test1.cpp
$ ./test1
Copy
2
1
这是一种比较特殊的用法,由于func7()采用的是值返回,因此在函数返回前将会产生一个临时对象,并执行一次拷贝构造函数。这样相当于a2引用了一个临时对象。前面曾经说过,临时对象将会在函数返回前被释放,但是为什么这里输出的结果是正常的呢?这是一种特殊情况,C++规定,如果有临时对象有一个引用,那么这个临时对象的生存期将延长到和这个引用相同。这样就可以解释上面的输出结果了:a2引用了一个临时对象,而不是引用了a1,因此a1的任何改变不会影响到a2。
注意:在VC编译环境下,const A& a2 = func7(a1);这行语句前面可以不加"const",但是在g++或者其他版本的编译器中不加"const"将会产生编译错误。加上"const"更加符合C++标准的规定,因为临时对象不可见,不允许通过该引用来改变临时对象的内容。
(8) 在main()函数中调用func8():
--------fun8--------------
$ g++ -o test1 test1.cpp
$ ./test1
Copy
Copy
2
1
通过以上的分析,对这个输出结果也就很好理解了:由于采用的是值参数,因此在函数体执行前会调用一次拷贝构造函数;采用的是值返回值,因此在函数返回前又会调用一次拷贝构造函数,这就是前两个"Copy"的由来。另外,a2引用的是一个临时对象,而不是引用了a1,因此a1的任何改变不会影响到a2。
(9)引用返回值赋值给对象:
会生成一个临时对象,然后再将临时对象赋值给主函数中的对象。
$ g++ -o test1 test1.cpp$ ./test1
Copy
2
总结一下:
如果使用引用接收 引用返回值,则返回的引用必须具有较长的生存期,不可以引用局部变量。
如果使用引用接收 值返回值,则引用了一个临时对象,该对象的生存期将延长到和这个引用相同。
- 引用参数与引用返回值
- 引用参数与引用返回值
- 引用参数与引用返回值
- 引用参数与引用返回值
- 引用参数与引用返回值
- 引用参数与引用返回值
- 引用参数与引用返回值
- c++引用参数与引用返回值
- [原创]引用参数与引用返回值
- 引用参数与引用返回值
- 引用参数与引用返回值,临时对象
- C++里引用参数与引用返回值
- 引用参数与引用返回值,临时对象
- “引用作为函数参数”与 “引用作为函数返回值”
- C++引用参数和引用返回值
- C++引用参数和引用返回值
- C++引用参数和引用返回值
- 值返回与引用返回
- perl 引用
- Oracle物理体系(二)
- java代码中调用受限制的JDK API
- 黑马程序员---java基础---异常处理
- 欧拉-拉格朗日方程
- 引用参数与引用返回值
- #define
- Linux进程间的通信(一)
- 整数求和程序的流程设计和实现
- 畅通工程续(1874)hdu最短路径算法—Floyd
- App Extension编程指南(iOS8/OS X v10.10):扩展类型--Today
- bzoj 1026——windy 数
- 『IOS』xcode 编译器禁用 ARC (Automatic Referencing Counting)
- mysql主从复制--傻瓜式操作