【问题标题】:How to identify if an object should be on the stack or not?如何识别一个对象是否应该在堆栈上?
【发布时间】:2010-10-11 16:15:24
【问题描述】:

我正在寻找在 C++ 中在堆栈或堆上分配对象的经验法则。我在这里找到了很多关于 SO 的讨论。很多人说,这关乎一个物体的生命周期。如果您需要比函数范围更长的生命周期,请将其放入堆中。这很有意义。

但让我感到困惑的是,很多人说,如果对象很小,就将它们分配给堆栈。如果对象很大,则将其放入堆中。但是他们都没有说如何识别物体是否大?

我有以下问题,

  1. 如何识别物体是否大?
  2. 堆栈的最大大小是多少?每个操作系统会有不同的堆栈大小?
  3. 我有一个包装器类,它包装了vector<string>。它将有大约 100 个项目。如果我将此类分配给堆栈,它会导致堆栈溢出吗?我试过这个,但效果很好。不确定我做错了什么。

【问题讨论】:

    标签: c++ stack heap-memory stack-size


    【解决方案1】:

    首先,向量(以及所有 STL 容器类)总是从堆中分配,所以您不必担心这一点。对于任何大小可变的容器,几乎不可能使用堆栈。

    如果您考虑堆栈分配的工作原理(在编译时,基本上是通过为每个对象递增一个指针),那么应该清楚向量内存来自堆。

    std::vector<int> myInts;
    std::string myString;
    SomeOther Class;
    
    // this memory must come from the heap, there's no way it
    // can now be allocated on the stack since there are other
    // objects and these parameters could be variable
    myString = "Some String";
    myInts.reserve(256);
    

    除非您在递归函数中,否则您可以将几千字节的数据放在堆栈上而不必担心。堆栈大小由程序(而不是操作系统)控制,默认值范围为 32kb - 1mb。大多数桌面软件的大小为 1mb。

    单个对象几乎从来不用担心。一般来说,它们要么对堆栈足够小,要么从堆内部分配。

    如果对象是函数的本地对象,则将它们放入堆栈。如果不把它们放在堆上。

    将堆用于为加载/排序/操作数据分配的大缓冲区。

    【讨论】:

    • ...用于手动加载/排序/操作数据。如果您使用 STL 为您完成工作,则无需费心费力地使用新/删除。只需在堆栈上分配即可。
    • 感谢您的回答。您是否有任何文档说向量在堆上分配内容?
    • 我已经更新了我的答案,以说明为什么向量(以及一般的集合)必须从堆中分配。
    • 这很有意义。谢谢
    • 两个挑剔 (1) 向量和字符串可能包含一个小的初始缓冲区,用于避免堆分配,在字符串中这称为小字符串优化,在向量中我不知道是否任何实现实际上都使用了这种优化。 (2) 在递归过程中,无论你在堆栈上放了多少东西(只要它小于堆栈帧),即使不是全部都被使用,整个堆栈帧也会被压入。
    【解决方案2】:

    根据MSDN 堆栈大小默认为1mb。 (这显然是给 Msdev 的)。

    从文章中可以看出,可以在编译时使用 /F 标志修改堆栈大小。

    我认为您关于堆栈与堆使用的第一个指南非常准确,前提是如果您的临时范围变量大于 mb,请将其粘贴在堆上(并且可能会问为什么要为首先是很短的时间)。

    【讨论】:

      【解决方案3】:

      在堆栈上分配大对象的唯一方法是在某些时候使用旧式数组。例如:

      void f() {
         char a[1000000];    // big object on the stack
      }
      
      struct A {
         char c[1000000];
      };
      
      void g() {
         A a;      // another big object on the stack
      }
      

      如果你不使用数组(你不应该),那么大多数东西都会在堆上为你分配:

      void h() {
         std::string s( 100000 );
      }
      

      上面在堆栈上分配几个字节用于指针、大小信息等,然后在堆上分配实际存储空间。

      所以别担心!你可能做对了!

      【讨论】:

      • 那些不是“旧式数组”。它们是数组。你不应该永远使用它们,你应该知道如何(以及何时)使用它们。
      • 感谢您的回答。但是你给出的最后一个代码怎么会进入堆?你的意思是说 std::string 在内部使用堆来保存内容?
      【解决方案4】:

      如何识别物体是否大?

      取决于您的编译器/平台组合。没有一个真正的限制。编译器通常允许您对此进行调整。

      堆栈的最大大小是多少?每个操作系统会有不同的堆栈大小?

      主要取决于上面。只是你对调音的控制较少。

      我有一个包装向量的包装类。它将有大约 100 个项目。如果我将这个类分配给堆栈,它会导致堆栈溢出吗?我试过这个,但效果很好。不确定我做错了什么。

      只要此包装器和该块中其他对象的总内存需求不超过您的堆栈帧大小,就可以工作。这又取决于平均字符串 sie。

      一个好主意是单步调试调试器并查看堆栈地址——这将在某种程度上为您提供宽度的开始。还有文档。

      【讨论】:

        【解决方案5】:

        1.如何识别物体是否大?

        使用“sizeof”

        class c {
          std::vector<std::string> s;
        };
        

        int size = sizeof(c);

        在我的机器上,“大小”是 16 个字节。

        2。堆栈的最大大小是多少?每个操作系统会有不同的堆栈大小?

        你看不出来,但这绝对不是分配大量数据的好地方。

        3。我有一个包装矢量的包装类。它将有大约 100 个项目。如果我将这个类分配给堆栈,它会导致堆栈溢出吗?我试过这个,但效果很好。不确定我做错了什么。

        没有。 std::vector 分配堆中的 100 个项目。

        【讨论】:

          【解决方案6】:

          我还要提到一点,因为vector&lt;&gt; 必须能够调整自身大小,如果它变得足够大(见下文),它必须使用堆来存储其包含的对象,即使vector&lt;&gt; 本身被声明为堆栈变量。

          [编辑] 正如 Motti 在评论中指出的那样,vector&lt;&gt; 可能会在其堆栈分配的对象中保留少量空间作为对小向量的优化。在这种情况下,使用足够小以适合此空间的向量将不需要任何堆分配。 (这个预先分配的空间必须非常小,以避免在处理较小的向量时浪费空间。)无论如何,如果向量变得足够大,它将需要在堆上(重新)分配。

          【讨论】:

          • 不正确,它可能使用作为对象一部分的初始缓冲区(如小字符串优化)。
          猜你喜欢
          • 2010-10-13
          • 2016-12-27
          • 2020-08-24
          • 1970-01-01
          • 2020-03-30
          • 2012-08-03
          • 2021-01-20
          • 1970-01-01
          • 2011-02-03
          相关资源
          最近更新 更多