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


116

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

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

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

1
如果将func1()更改为使用动态分配的内存,则它们是相同的:-)int& i = * new int;
Martin York

Answers:


193

此代码段:

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()返回原始值的相同方式返回大对象是完全可以的,因为当今几乎每个编译器都实现某种形式的返回值优化

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引用是完全合法的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();  
}

2
美丽的解释。:hattip:在第三个代码段中,您要删除int* p = func2(); delete p;现在,当删除'p'时,是否意味着分配在函数func2()定义内的内存也被删除了?
Aquarius_Girl

2
@Anisha Kaul:是的。内存在内部分配func2(),在下一行释放到外部。但是,这是处理内存的一种非常容易出错的方式,就像我说过的那样,您将改用RAII的某种变体。顺便说一句,您听起来好像在学习C ++。我建议选择一本很好的C ++入门书以供学习。另外,如果您有问题,以备将来参考,可以随时将问题发布到堆栈溢出中。评论不是要提出全新的问题。
2011年

现在我明白了,您做对了!该函数正在返回一个指针,并且在该函数之外,您已经删除了它所指向的内存。现在很清楚,感谢您的链接。
Aquarius_Girl

并且您已经编辑了答案??:mad:我很容易错过它。;);)
Aquarius_Girl

@Anisha Kaul:不,我没有。根据帖子下方的时间戳,我最后一次编辑答案是在1月10日。
2011年

18

局部变量是堆栈上的内存,超出范围时不会自动使该内存无效。从嵌套更深的Function(在内存中的栈中更高)开始,访问此内存是绝对安全的。

一旦函数返回并结束,事情就会变得很危险。通常,当您返回时,内存不会被删除或覆盖,这意味着地址中的内存仍包含您的数据-指针似乎有效。

直到另一个函数建立了堆栈并覆盖了它。这就是为什么它可以工作一会儿的原因-然后在一个特别深层嵌套的函数集或一个具有非常大的尺寸或许多本地对象的函数再次到达该堆栈内存后突然停止起作用。

甚至可能再次到达同一程序部分,并用新的函数变量覆盖旧的本地函数变量。所有这一切都是非常危险的,应该强烈反对。 不要使用指向本地对象的指针!


2

这些简单的规则值得记住,它们适用于参数和返回类型...

  • 价值-制作相关商品的副本。
  • 指针-指相关商品的地址。
  • 参考-从字面上看是有问题的项目。

每个都有时间和地点,因此请确保您了解它们。正如您在此处显示的那样,局部变量仅限于它们在函数作用域中局部存在的时间。在您的示例中,返回类型为int*and returning &i同样是不正确的。在这种情况下,您最好这样做...

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

这样做将直接更改您传入参数的值。而此代码...

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.