是在堆栈或堆上分配的不使用“新”分配的对象

Posted

技术标签:

【中文标题】是在堆栈或堆上分配的不使用“新”分配的对象【英文标题】:are objects allocated without using "new" allocated on stack or heap 【发布时间】:2017-02-21 14:54:12 【问题描述】:

我是 C++ 新手,所以问题很简单。

假设我已经定义了一个类 Foo,并在以下代码中创建了一个向量向量:

  namespace testme 
        class Foo 
           public:
            Foo(int x): x_(x)  ;
            static vector<vector<int>> ReturnVecOfInts(int num) 
               vector<vector<int>> ret(num);
               for (int i = 0; i < num; i++) 
                 vector<int> tmp;
                 ret.push_back(tmp);
                
                return ret;
           
  

当我打电话时:

    Foo::ReturnVecOfInts(5)

是在堆或堆栈上创建的向量的向量。引用在堆栈上,但我想知道它是否指向堆,因为我想从函数中返回这个对象。

这对我很重要,因为很明显,如果将它们分配在堆栈上,向量的向量将超出范围,并且在被调用函数之外无法使用。

【问题讨论】:

完全取决于该声明所在的位置。如果它在命名空间范围内,则该对象具有静态存储持续时间,既不是堆栈也不是堆。顺便说一句,请不要发布幻想代码:发布真实代码。 c++ 标准中没有定义 stackheap 之类的东西。 这取决于您使用的系统。这就是为什么 C++ 没有堆栈和堆的概念,而是使用自动和动态存储。 通常如果您在函数内部声明它在堆栈上。如果它在堆分配的对象内,那么它也在堆上。这在很大程度上取决于该语句出现的上下文。 所以你真正的问题是:按值返回本地对象是否安全?,是吗? 【参考方案1】:

你可能想多了。

在你的情况下,你在这样的代码中是安全的:

Foo f = Bar::GetFoo();

当您从函数返回对象时,通常可能会发生各种优化(例如 RVO、NRVO 等),但最重要的是您的 f 对象可以安全使用。

即使在 Foo 内部你有一个像 std::vector 这样的数据成员,它通常从堆中分配它的内存(你可以使用自定义分配器自定义这种行为),这要归功于复制构造函数、移动构造函数、析构函数等。你完全安全地从函数返回它,或者复制 Foo 实例。

编辑我注意到您在我写完答案后更改了代码,返回 vector&lt;vector&lt;int&gt;&gt; 而不是 Foo。同样,我写的内容仍然适用。

【讨论】:

从@Ryan Haining 的评论中,我了解到c++ 返回的是实际对象。返回 vector> 会返回一个对 vector 的引用的矢量,该矢量是在堆栈上分配的或者不会超出范围的 vector 类型的对象的矢量。 vector> 由嵌套向量组成。内部向量是 vector,它们使用默认分配器从堆中分配一个连续的整数块。外部向量是“vector”的向量,所以在这种情况下内存布局更加复杂。但底线是从函数返回 vector> 很好,这要归功于 std::vector 的正确实现细节。 @CaptainJacksparrow 基本上,vector 包装和管理一个动态分配的数组;可以安全地假设数组将在堆(或等效)上创建,管理部分(包括数组的地址)在堆栈(或等效)上;管理部分是唯一需要返回的东西,因为数组不会被释放。 [请注意,这适用于具有堆栈和堆的系统。更深奥的系统可能会以不同的方式处理std::vector。另请注意,它在实践中的工作方式可能与此不同;这只是一个简单的描述。] 想象一下:你的客厅里有一台电视,你和你的朋友正在看它。你有遥控器。如果你想让你的朋友负责电视,你是把电视放在他腿上,还是把遥控器给他?

以上是关于是在堆栈或堆上分配的不使用“新”分配的对象的主要内容,如果未能解决你的问题,请参考以下文章

是否在C中的堆栈或堆上创建激活记录?

c#结构/类堆栈/堆控制?

C 数组实例化 - 堆栈或堆分配?

堆和堆栈内存是如何管理、实现和分配的?

在共享内存中共享std :: string

在递归函数中在堆上分配与在堆栈上分配