C++函数的返回值是局部变量时,该返回值可能是值类型、指针类型和引用类型。
以自定义类型MyStruct为例:
struct MyStruct
{
int x;
int y;
};
1 返回值是自定义结构的值类型
定义一个返回值为MyStruct的函数MyProc1()。
MyStruct MyProc1()
{
MyStruct l_mystruct;
l_mystruct.x = 1;
l_mystruct.y = 2;
return l_mystruct;
}
MyProc1()
的返回值是局部变量
myStruct
。在
main()
函数中,调用该函数
MyStruct myStruct = MyProc1();
在《C++
函数的返回值(上)》一文中提到使用临时对象(
temporary object
)来保存函数的返回值。函数的返回值用于初始化调用点的一个临时对象,该临时对象就是函数调用的结果。所以,当函数返回值是自定义的值类型时,即使函数的返回值是局部变量,其返回值也是有效值,如图
1
、图
2
所示。
图1 函数返回值是自定义结构的值类型时的流程
图2 myStruct的值
2 返回值是自定义结构的指针类型
定义一个返回值是MyStruct*的函数MyProc2()。
MyStruct* MyProc2()
{
MyStruct* l_pMyStruct =new MyStruct();
l_pMyStruct->x = 1;
l_pMyStruct->y = 2;
return l_pMyStruct;
}
在MyProc2()
中,通过
new
关键字在堆(
heap
)上分配了内存,并且将该内存的地址保存到局部变量
l_pMyStruct
中,对内存进行赋值之后,返回该内存的地址。
在main()函数中,调用MyProc2()函数。
MyStruct* pMyStruct = MyProc2();
此时,pMyStruct
的值是
MyProc2()
中在堆上分配的内存地址。因为当函数
MyProc2()
结束后,保存堆内存地址的局部变量
l_pMyStruct
被销毁,而堆内存本身不会被销毁。如图
3
、图
4
所示。
图3 MyProc2()函数返回值
图4 pMyStruct指向的内存值
3 返回值是自定义结构的引用类型
当函数的返回值是自定义结构的引用类型时,可以将该值赋值给值类型,也可以复制给引用类型。定义一个返回值是MyStruct引用的函数MyProc3()。
MyStruct& MyProc3()
{
MyStruct l_myStruct_ref;
l_myStruct_ref.x = 3;
l_myStruct_ref.y = 4;
return l_myStruct_ref;
}
在该函数中,返回的是局部变量l_myStruct_ref
的引用。
3.1 将返回值赋值给值类型
在main()函数中,将MyProc3()函数的返回值赋值给MyStruct结构的对象。
MyStruct myStruct3 = MyProc3();
正如在“
1
返回值是自定义结构的值类型”中提到的,当函数返回时,使用临时对象来保存局部变量
l_myStruct_ref
的引用,之后将
myStruct3
的成员变量
x
和
y
的值赋值成
l_myStruct_ref
对应的成员变量值。接下来虽然局部变量
l_myStruct_ref
被销毁,但是
myStruct3
的值却保留了下来,所以
myStruct3
的
x
和
y
的值依然是
3
和
4
。
3.2 将返回值赋值给引用类型
在main()函数中,将MyProc3()函数的返回值赋值给MyStruct结构的引用类型。
MyStruct& myStruct_ref = MyProc3();
当函数返回时,使用临时对象来保存局部变量l_myStruct_ref
的引用,而变量
myStruct_ref
中保存的是临时变量的引用,其实也就是局部变量
l_myStruct_ref
的引用,当局部变量
l_myStruct_ref
被销毁,则
myStruct_ref
中的值同时被销毁,如图
5
所示。
图5 myStruct_ref与l_myStruct_ref的关系
从代码中也可以看出,当执行完如下代码后,
MyStruct& myStruct_ref = MyProc3();
虽然此时myStruct_ref
的成员变量值依然是
3
和
4
,如图
6
所示。但是,
该地址已经被标记为可修改,之后再执行其他代码时,这个地址可能就会被修改为其他值
,如图7
所示。
图6 MyProc3()函数返回后的内存
图7 执行了其它语句后的内存
所以,《C++Primer第5版中文版》中提到,不要返回局部对象的引用。