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

时间:2011-01-10 04:36:14

标签: c++

如果必须返回i,以下代码(func1())是否正确?我记得在某处读到返回对局部变量的引用时存在问题。它与func2()有什么不同?

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

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

3 个答案:

答案 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本身。