众所周知,C++函数可以传入引用参数和返回引用。
函数引用参数避免了过多的指针操作,对加强函数的可读性很有帮助;另外,在传入体积很大的类型的变量时,引用参数可以避免拷贝对象,加快程序运行效率。
函数支持引用型的返回值是为什么呢?这个问题要一分为二:对于类类型的引用返回值,函数可以使用在使用重载运算符的串联表达式中,而不必担心构造多个对象。
#include <stdio.h>
#include <iostream>
#include <iostream>
using namespace std;
class Rec
{
public:
int a;
int b;
friend ostream & operator<<(ostream &os,Rec& b)
{
os<<"["<<b.a<<"]";
return os;
}
};
Rec & funRec(Rec& obj)
{
return obj;
}
{
return obj;
}
int main(int argc , char* args[])
{
{
Rec obj;
Rec *ptr = &( funRec(obj) );
Rec ano = funRec(obj) ;
printf("class obj vs ptr vs ano: 0X%0X , 0X%0X , 0X%0X ",obj,*ptr,ano);
Rec *ptr = &( funRec(obj) );
Rec ano = funRec(obj) ;
printf("class obj vs ptr vs ano: 0X%0X , 0X%0X , 0X%0X ",obj,*ptr,ano);
运行结果:
class obj vs ptr vs ano: 0XCCCCCCCC , 0XCCCCCCCC , 0XCCCCCCCC
可见,黄褐色3句代码只会生成一个对象,因为fucRec传入引用参数,返回引用对象。 如果返回的不是引用对象,那么在Rec ano = funRec(obj)“=”的时候就需要调用拷贝构造函数了,而且Rec *ptr = &( funRec(obj) );编译不过。
但是,对于返回基本类型如int型的函数,返回引用类型就变得非常迷惑了。
有代码:
int &func(int& a)
{
return a;
}
{
return a;
}
int main(int argc , char* args[])
{
int a=0;
int b=func(a);
printf("&a=0X%08X,&b=%08X/n",&a,&b);
}
运行结果:
&a=0X0012FF7C,&b=0012FF78
这里可以看到,返回变量的地址和传入地址是不同的。变量b是main函数中自己的栈中生成的。
这一点从汇编代码片段中可以看得更清楚:
; Line 38
lea eax, DWORD PTR _a$[ebp]
push eax
call ?func@@YAAAHAAH@Z ; func
add esp, 4
mov ecx, DWORD PTR [eax]
mov DWORD PTR _b$[ebp], ecx
lea eax, DWORD PTR _a$[ebp]
push eax
call ?func@@YAAAHAAH@Z ; func
add esp, 4
mov ecx, DWORD PTR [eax]
mov DWORD PTR _b$[ebp], ecx
事实上,func函数有无引用返回,其编译出来的汇编代码都是一样的。引用号在此没有特别的作用,除了带来误解以外。
总结:对于类对象,尽可能使用引用类型参数和返回;在基础类型中,尽量不要使用,以免引起误解。
C++语言的函数返回值类型可以分为内部类型和自定义类型两大类。
在函数返回内部类型中不能返回数组类型但可以返回指向数组的指针,同样也可以返回指向函数的函数指针。如果希望返回值可以作为左值(即可以放在赋值操作符左边的)那就必须返回引用类型。
而在函数返回自定义类型(即返回类类型)中根据是否可作为左值,返回值是否可调用成员函数的不同可分为以下四种情况。
在函数返回内部类型中不能返回数组类型但可以返回指向数组的指针,同样也可以返回指向函数的函数指针。如果希望返回值可以作为左值(即可以放在赋值操作符左边的)那就必须返回引用类型。
而在函数返回自定义类型(即返回类类型)中根据是否可作为左值,返回值是否可调用成员函数的不同可分为以下四种情况。
T:返回类类型
T f();
const T f();
T& f();
const T& f();
T f(); 返回一般的类类型,返回的类类型不能作为左值,但返回的类类型可以直接调用成员函数来修改,如function().set_Value(); 返回类类型调用复制构造函数。
const T f(); 此种类型与上述第一种相同,唯一不同的是返回的类类型不能调用成员函数来修改,因为有const限定符。
T& f(); 返回类的引用可以作为左值,并且返回的类类型引用可以直接调用成员函数来修改,返回的类类型不会调用复制构造函数。
const T& f(); 不能作为左值,不能调用成员函数修改,不会调用复制构造函数。
T f();
const T f();
T& f();
const T& f();
T f(); 返回一般的类类型,返回的类类型不能作为左值,但返回的类类型可以直接调用成员函数来修改,如function().set_Value(); 返回类类型调用复制构造函数。
const T f(); 此种类型与上述第一种相同,唯一不同的是返回的类类型不能调用成员函数来修改,因为有const限定符。
T& f(); 返回类的引用可以作为左值,并且返回的类类型引用可以直接调用成员函数来修改,返回的类类型不会调用复制构造函数。
const T& f(); 不能作为左值,不能调用成员函数修改,不会调用复制构造函数。
有一个函数
... foo()
并且这个函数返回一个引用...
... & foo()
...., 一个指向位图(Bitmap)的引用 ...
Bitmap & foo()
.... 并且这个位图(bitmap)是常量
const Bitmap & foo ()
... foo()
并且这个函数返回一个引用...
... & foo()
...., 一个指向位图(Bitmap)的引用 ...
Bitmap & foo()
.... 并且这个位图(bitmap)是常量
const Bitmap & foo ()
当然你也可以用指针来做同样的事情:
const Bitmap * foo()
foo 返回一个指针 ... 指向一个Bitmap ... 并有这个Bitmap是个常量.
Bitmap * const foo()
foo 返回某个东西,这个东西是常量 ... 这个东西又是指针 ... 一个指向Bitmap的指针.
const Bitmap * const foo()
foo 返回某个东西,这个东西是常量 ... 这个东西又是指针 ... 一个指向Bitmap的指针.
....
并且这个Bitmap也是常量.
*****************临时对象的本质**************************
在函数中返回一个对象时,因为该对象是局部对象,,离开了它的函数就会消失,不可能返回到调用函数中而继续存在。对此种情况编译系统会在主函数中创建一个临时的无名对象,该临时对象的生命期仅仅在一个表达式中。