【问题标题】:returning local variables返回局部变量
【发布时间】:2011-06-22 19:18:06
【问题描述】:

我今天遇到了一个关于局部变量的问题。我知道了...

 int * somefunc()
 {
     int x = 5;
     return &x;
 }

 int * y = somefunc();
 //do something

很糟糕,不安全等。我想这种情况对于......

int * somefunc()
{
    int * x = new int;
    x = 5;
    return x;
}

int * y = somefunc();
//do something
delete y;

很长一段时间以来,我一直认为这是安全的,因为 x 的地址在返回时仍然在范围内。但是,我现在有了第二个想法,我认为这会导致内存泄漏和其他问题,就像第一个示例一样。有人可以帮我确认一下吗?

【问题讨论】:

  • 不,这很好,但如果可能,更喜欢智能指针指向new/delete。这样可以最大限度地减少泄漏的可能性并清理代码。
  • 第二个很好,除了x = 5;。那应该是*x = 5;。我同意史蒂夫关于智能指针的观点。

标签: c++ pointers scope


【解决方案1】:

就目前而言,第二个例子是错误的。你可能是这个意思:

int * somefunc()
{
    int * x = new int;
    *x = 5; // note the dereferencing of x here
    return x;
}

现在这在技术上没问题,但很容易出错。首先,如果在分配x 后发生异常,您必须捕获它,删除x 然后重新抛出,否则会导致内存泄漏。其次,如果你返回一个指针,调用者必须删除它——调用者忘记了。

推荐的方法是返回一个智能指针,例如boost::shared_ptr。这将解决上述问题。要了解原因,请阅读RAII

【讨论】:

  • std::auto_ptr(或在新标准中的替代品std::unique_ptr)也可以在这里使用。
  • 我实际上并不确定是否需要取消引用。我想我现在实际上并没有编写程序,所以为了这个问题,这很好。反正你们都知道我的意思:)
  • @MGZero:对不起,这让我有点生气。我们花时间在这里帮助人们解决他们的问题。如果提问的人让我们猜测他们的程序中的错误是由于懒惰造成的拼写错误,或者是导致他们问题的实际错误,那么这真的很令人沮丧。今后请不要这样做。阅读this page 两遍,每次提出问题时都想一想。
  • 对不起,我觉得你有点反应过度了。谢谢您的回答。美好的一天。
【解决方案2】:

是的,您冒着内存泄漏的风险。 (除了编译错误。) 对 int 这样做很傻,但即使是大型结构,原理也是一样的。

但请理解:您编写了 C 风格的代码,其中有一个分配存储的函数。

如果你想学习 C++,你应该把 somefunc() 和它操作的数据放到一个类中。方法和数据在一起。正如 Space_C0wb0y 指出的那样,一个类也可以做 RAII。

【讨论】:

  • 所有函数不一定要放在一个类中。
  • 不,我不是想学习 C++,我已经用了 6 年了。我只是想知道今天出现的一个普遍问题。不过,谢谢!
【解决方案3】:

您可能只是将 int * 作为示例,但实际上,在您提到的情况下,没有理由返回 int *,只需返回 int,实际值就足够了。我经常看到这些情况,变得过于复杂,什么时候,真正需要的只是简化。

在'int *'的情况下,我只能真正想到一个返回整数数组的实际情况,如果是这样,那么你需要分配它,返回它,希望在你的文档中,注意它必须被释放。

【讨论】:

  • 如果此代码在共享库中,您需要自己提供释放函数。该库可能在​​与库调用者不同的条件下编译,因此newdelete 的系统会发生冲突。
  • @Chris Lutz - 是的,非常好的观点,不记得所有内容。
【解决方案4】:

正如您现在所知道的,第一种方法肯定会导致问题。

第二个是好的,但需要程序员注意,因为他需要显式删除返回的指针(就像你做的那样)。当您的应用程序变得更大时,这会更加困难,使用这种方法可能会导致问题(内存泄漏),因为程序员会发现很难跟踪他需要释放的每个变量。

这种情况的第三种方法是 pass a variable by reference 在函数内部使用,这样更安全。

void somefunc(int& value)
{
    value = 5;
}

// some code that calls somefunc()
int a_value = 0;
somefunc(a_value);
// printing a_value will display 5 

【讨论】:

    【解决方案5】:

    (已编辑)

    是的,第二个很好,只要你在分配之前取消引用那个“x”!

    【讨论】:

    • 等等,new操作符不是在堆上分配内存,不是在栈上?
    • -1 第二个是(alnost)从函数传回指针的正确方法。 (x = 5 必须是 *x = 5 但其他是正确的。)
    • 啊,我假设除了错字之外还有更多的混乱。
    【解决方案6】:

    好的,我将通过回答以下问题来分析:

    1. x 包含什么? - 一个内存位置(因为它是一个指针 变量)
    2. x 的范围是什么? - 因为它是一个自动变量,所以它的范围是 仅限于函数 somefunc()
    3. 自动变量退出本地范围后会发生什么? - 他们是 从堆栈空间中删除。
    4. 那么从 somefunc() 返回后 x 现在会发生什么? - 因为它是 在堆栈上声明的自动变量 ,它的范围(寿命)仅限于 somefunc() ,因此将被删除。
    5. 好的,现在,x 指向的值会发生什么变化?我们有一个 分配值时的内存泄漏 在堆上,我们刚刚失去了 删除 x 时的地址。
    6. 你得到了什么? - 不知道。
    7. 删除 y 后会发生什么? - 不知道。

    【讨论】:

      【解决方案7】:

      重点是不要返回指向局部变量的指针或引用,因为一旦函数返回,局部变量就不存在了。

      但是,返回值仍然存在,动态分配的内存当然也存在。

      在 C++ 中,我们希望尽可能避免使用原始指针。要“返回一个已经存在的值”(即函数不创建新值),请使用引用。要“返回一个不存在的值”(即函数创建一个新值,在惯用意义上,而不是 new 关键字意义上)使用一个值,或者如果需要,使用某种智能指针包装器。

      【讨论】:

        【解决方案8】:

        既是内存泄漏又是崩溃(因为删除)。

        【讨论】:

        • 事实上,两者都不是。它要么是编译错误,要么(如果 OP 表示 *x = 5 而不是所写的)完全没问题。
        • 哦,我错过了它需要演员表 :)
        猜你喜欢
        • 2021-11-07
        • 2014-05-14
        • 1970-01-01
        • 1970-01-01
        • 2013-10-03
        • 2017-06-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多