在 C + + 中返回对局部变量的引用

如果必须返回 i,下面的代码(Function 1())是否正确?我记得在什么地方读到过,当返回对局部变量的引用时会出现问题。它和 Function 2()有什么不同?

int& func1()
{
int i;
i = 1;
return i;
}


int* func2()
{
int* p;
p = new int;
*p = 1;
return p;
}
152582 次浏览

下面的代码片段:

int& func1()
{
int i;
i = 1;
return i;
}

将无法工作,因为您正在返回一个别名(引用)到一个生存期受限于函数调用范围的对象。这意味着一旦 func1()返回,int i就会死亡,使得从函数返回的引用变得毫无价值,因为它现在引用了一个不存在的对象。

int main()
{
int& p = func1();
/* p is garbage */
}

第二个版本确实有效,因为变量是在空闲存储区上分配的,而空闲存储区不绑定到函数调用的生存期。但是,您要负责 deleteing 分配的 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
}

通常您会将指针包装在某个 拉尔类和/或工厂函数中,这样您就不必自己将它包装为 delete

在这两种情况下,您都可以返回值本身(尽管我意识到您提供的示例可能是人为的) :

int func3()
{
return 1;
}


int main()
{
int v = func3();
// do whatever you want with the returned value
}

请注意,返回大对象的方式与 func3()返回原始值的方式完全一样,因为现在几乎每个编译器都实现了某种形式的 返回值优化:

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();
}

有趣的是,将临时参考绑定到 康斯特参考是 完全合法的 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();
}

局部变量是堆栈上的内存,当您超出作用域时,该内存不会自动失效。从嵌套的 功能更深处(在内存堆栈的更高处) ,访问这个内存是完全安全的。

但是一旦函数返回并结束,事情就变得危险了。 通常当你返回时,内存并没有被删除或者覆盖,这意味着这个地址的内存仍然包含你的数据——指针看起来是有效的。

直到另一个函数构建堆栈并覆盖它。 这就是为什么它可以工作一段时间,然后突然停止后,一个特别深嵌套的函数集,或一个函数的大小真的很大或许多本地对象,达到堆栈内存再次功能。

甚至可能再次到达相同的程序部分,并用新的函数变量覆盖旧的本地函数变量。所有这些都是非常危险的,应该大力劝阻。

不要使用指向本地对象的指针!

需要记住的是这些简单的规则,它们同时适用于参数和返回类型..。

  • 值——创建所涉项的副本。
  • 指针-指向相关项目的地址。
  • 参考-是字面上的问题项目。

每个人都有自己的时间和地点,所以一定要了解他们。正如您在这里所展示的,局部变量仅限于它们在函数作用域中局部活动的时间。在您的示例中,返回类型为 int*并返回 &i同样是不正确的。如果那样的话,你最好这么做。

void func1(int& oValue)
{
oValue = 1;
}

这样做将直接更改传递给参数的值。

void func1(int oValue)
{
oValue = 1;
}

不会。它只是将 oValue本地的值更改为函数调用。这样做的原因是因为您实际上只是更改了 oValue的一个“本地”副本,而不是 oValue本身。