【问题标题】:Scope and return values in C++C++ 中的作用域和返回值
【发布时间】:2010-09-21 11:19:59
【问题描述】:

我重新开始使用 c++ 并且正在考虑变量的范围。 如果我在函数中有一个变量,然后我返回该变量,该变量在返回时是否不会“死”,因为它所在的范围已经结束?

我已经尝试过使用返回字符串的函数,它确实有效。 谁能解释一下?或者至少请给我指出一个可以向我解释这一点的地方。

谢谢

【问题讨论】:

    标签: c++ return-value scope


    【解决方案1】:

    这取决于退货商品的类型。如果您按值返回,则会创建一个新的变量副本以返回给调用者。我认为您不需要担心对象的生命周期,但您可能需要担心复制对象的成本(但请不要过早优化 - 正确性更为重要):

    std::string someFunc( std::string& const s)
    {
        return s + "copy";
    }
    

    如果函数返回一个引用,那么你需要小心你返回的内容,因为它的生命周期需要超出函数的生命周期,如果你是,调用者不一定能够delete它使用new 创建对象:

    std::string& someFunc2( std::string const& s)
    {
        return s + "reference to a copy";   // this is bad - the temp object created will 
                                            //  be destroyed after the expression the 
                                            //  function call is in finishes.
                                            //  Some, but not all, compilers will warn 
                                            //  about this.
    }
    

    当然,返回指针也会有类似的生命周期考虑。

    【讨论】:

      【解决方案2】:

      只是为了多一点面向内存模型的解释:当调用函数时,会为函数腾出一个临时空间来放置其局部变量,称为 frame。当函数(被调用者)返回其值时,将返回值放入调用它的函数(调用者)的框架中,然后被调用者框架被销毁。

      “框架被破坏”部分是为什么您不能从函数返回指针或对局部变量的引用。指针实际上是一个内存位置,因此一旦帧被销毁,返回局部变量(根据定义:帧内的变量)的内存位置就变得不正确。由于被调用者框架在返回值后立即被销毁,因此任何指向局部变量的指针或引用都会立即不正确。

      【讨论】:

        【解决方案3】:

        当函数终止时, 发生以下步骤:

        • 函数的返回值为 复制到原来的占位符中 为此目的放入堆栈。

        • 堆栈帧之后的所有内容 指针弹出。这破坏 所有局部变量和参数。

        • 返回值被弹出 堆栈并分配为值 的功能。如果值 函数没有分配给任何东西, 没有分配发生,并且 价值丢失。

        • 下一条指令的地址 执行从堆栈中弹出, CPU 恢复执行 那条指令。

        The stack and the heap

        【讨论】:

        • 这篇文章不错!我为它添加了书签,以便与了解堆栈的人分享 :)
        • 标准 C++ 没有堆栈的概念。
        • @CMS 我很好奇 C++ 标准中是否有部分支持此声明?据我所知,有关堆栈的这些信息通常是特定 CPU 架构的调用约定的一部分,并且不必对每个可用平台都相同。
        【解决方案4】:

        这实际上取决于您返回的变量类型。如果您返回一个原语,那么它是通过复制而不是通过引用返回的,因此该值被复制到调用函数可以获取它的堆栈顶部(或者,更常见的是放入寄存器中)。如果您在堆上分配一个对象或内存并返回一个指针,那么它不会死,因为它在堆上,而不是在堆栈上。但是,如果您在堆栈上分配一些东西并返回它,那将是一件坏事。例如,其中任何一个都非常糟糕:

        int *myBadAddingFunction(int a, int b)
        {
            int result;
        
            result = a + b;
            return &result; // this is very bad and the result is undefined
        }
        
        char *myOtherBadFunction()
        {
            char myString[256];
        
            strcpy(myString, "This is my string!");
            return myString; // also allocated on the stack, also bad
        }
        

        【讨论】:

        • 我实际上是在返回一个字符串。它不完全是一个数组,但我在函数中声明它,如下所示: string funcnam(parms...) { string test; 返回字符串;并且一切正常
        • 如果它是一个实际的字符串对象,它会很好,因为它是在堆上分配的,而不是在堆栈上。在上面的坏例子中,myString 实际上并不是一个对象,只是一个标准的 char 数组。 CMS 的 Stack and Heap 文章非常好,您应该阅读一下 :)
        【解决方案5】:

        当您返回一个值时,会生成一个副本。局部变量的作用域结束,但会创建一个副本,并返回给调用函数。示例:

        int funcB() {
          int j = 12;
          return j;
        }
        
        void A() {
          int i;
          i = funcB();
        }
        

        将 j (12) 的值复制并返回给 i,以便 i 收到 12 的值。

        【讨论】:

        • 如果返回值是一个数组怎么办?我需要在返回之前对其进行 malloc 吗?因为数组保存了内存的第一个地址
        • 您将动态分配一个数组(new / malloc)。您的动态分配将为局部变量分配一个指针值,并且该指针的值将被返回。返回一个本地声明的数组 (int[] var; std::array othervar;) 将是一个错误 - 函数退出时作用域将结束,您的数组将无效。
        • 谢谢,所以如果i 在这种情况下是一个结构对象,那么我将&i 作为一个参数传递给funcB - funcB 不会返回任何东西,只是使用i结构成员计算,我需要malloci吗?没有权利?因为i的值还在void A()的范围内,所以我不怕在funcB结束后生命周期结束
        • 对 - 你的解决方案比让 funcB() 到 malloc 并返回一个指针更好。如果你不在同一个地方分配和释放内存,很容易发生内存泄漏。
        【解决方案6】:

        局部变量被复制到返回值。复制构造函数被调用用于非平凡的类。

        如果您返回一个指针或对局部变量的引用,您将遇到麻烦——正如您的直觉所建议的那样。

        【讨论】:

          猜你喜欢
          • 2012-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-04
          • 1970-01-01
          • 2019-04-17
          • 2011-02-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多