如果必须返回i,以下代码(func1())是否正确?我记得在某处读到返回对局部变量的引用时存在问题。它与func2()有什么不同?
int& func1()
{
int i;
i = 1;
return i;
}
int* func2()
{
int* p;
p = new int;
*p = 1;
return p;
}
答案 0 :(得分:165)
此代码段:
int& func1()
{
int i;
i = 1;
return i;
}
将无效,因为您将一个别名(引用)返回给一个生命周期限制在函数调用范围内的对象。这意味着一旦func1()
返回,int i
就会死亡,使得从函数返回的引用毫无价值,因为它现在引用了一个不存在的对象。
int main()
{
int& p = func1();
/* p is garbage */
}
第二个版本确实有效,因为变量是在免费存储上分配的,它不受函数调用的生命周期的约束。但是,您负责delete
分配的int
。
int* func2()
{
int* p;
p = new int;
*p = 1;
return p;
}
int main()
{
int* p = func2();
/* pointee still exists */
delete p; // get rid of it
}
通常,您可以将指针包装在某个RAII类和/或工厂函数中,这样您就不必自己delete
。
在任何一种情况下,您都可以返回值本身(虽然我意识到您提供的示例可能是人为的):
int func3()
{
return 1;
}
int main()
{
int v = func3();
// do whatever you want with the returned value
}
请注意,以func3()
返回原始值的方式返回大对象是完全正常的,因为现在几乎每个编译器都实现某种形式的return value optimization:
class big_object
{
public:
big_object(/* constructor arguments */);
~big_object();
big_object(const big_object& rhs);
big_object& operator=(const big_object& rhs);
/* public methods */
private:
/* data members */
};
big_object func4()
{
return big_object(/* constructor arguments */);
}
int main()
{
// no copy is actually made, if your compiler supports RVO
big_object o = func4();
}
有趣的是,将临时绑定到 const 引用是perfectly legal C++。
int main()
{
// This works! The returned temporary will last as long as the reference exists
const big_object& o = func4();
// This does *not* work! It's not legal C++ because reference is not const.
// big_object& o = func4();
}
答案 1 :(得分:15)
局部变量是堆栈中的内存,当您超出范围时,该内存不会自动失效。从功能更深的嵌套(内存中的堆栈更高),它可以非常安全地访问此内存。
一旦函数返回并结束,事情就会变得危险。 通常,当您返回时,内存不会被删除或覆盖,这意味着该地址的内存仍然包含您的数据 - 指针似乎有效。
直到另一个函数构建堆栈并覆盖它。 这就是为什么它可以工作一段时间 - 然后突然停止运行一个特别深度嵌套的函数集,或一个真正大型或许多本地对象的函数,再次到达堆栈内存。
甚至可能会再次到达相同的程序部分,并使用新的函数变量覆盖旧的本地函数变量。所有这些都是非常危险的,应该严重劝阻。 不要使用指向本地对象的指针!
答案 2 :(得分:1)
要记住的一件好事是这些简单的规则,它们适用于参数和返回类型......
每个人都有时间和地点,所以一定要了解它们。正如您在此处所示,局部变量仅限于它们在函数范围内本地存活的时间。在您的示例中,返回类型为int*
并且返回&i
同样不正确。在这种情况下你会做得更好......
void func1(int& oValue)
{
oValue = 1;
}
这样做会直接更改传入参数的值。而这段代码......
void func1(int oValue)
{
oValue = 1;
}
不会。它只会更改函数调用的oValue
local的值。这样做的原因是因为你实际上只是在改变一个" local" oValue
的副本,而不是oValue
本身。