当前位置:  开发笔记 > 编程语言 > 正文

在C++中适当的堆栈和堆使用?

如何解决《在C++中适当的堆栈和堆使用?》经验,为你挑选了5个好方法。

我已经编程了一段时间,但它主要是Java和C#.我实际上从来没有必须自己管理内存.我最近开始用C++编程,我有点困惑的是什么时候我应该把东西存放在堆栈上以及何时将它们存储在堆上.

我的理解是,非常频繁访问的变量应该存储在堆栈中,对象,很少使用的变量和大型数据结构都应该存储在堆上.这是正确的还是我错了?



1> Crashworks..:

不,堆栈和堆之间的区别不是性能.它的生命周期:函数内部的任何局部变量(任何不是malloc()或new的东西)都存在于堆栈中.从函数返回时它会消失.如果你想要比声明它的函数更长寿,你必须在堆上分配它.

class Thingy;

Thingy* foo( ) 
{
  int a; // this int lives on the stack
  Thingy B; // this thingy lives on the stack and will be deleted when we return from foo
  Thingy *pointerToB = &B; // this points to an address on the stack
  Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap.
                                     // pointerToC contains its address.

  // this is safe: C lives on the heap and outlives foo().
  // Whoever you pass this to must remember to delete it!
  return pointerToC;

  // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. 
  // whoever uses this returned pointer will probably cause a crash!
  return pointerToB;
}

为了更清楚地理解堆栈是什么,从另一端来看它 - 而不是试图理解堆栈在高级语言方面的作用,查找"调用堆栈"和"调用约定",看看是什么当你调用一个函数时,机器真的会这样做.计算机内存只是一系列地址; "堆"和"堆栈"是编译器的发明.


当然,new/malloc()本身是一个缓慢的操作,并且堆栈比任意堆线更可能在dcache中.这些是真正的考虑因素,但通常是生命问题的次要因素.
很好的解释,尽管在频繁分配和/或解除分配的多线程场景中,堆*是一个争用点,从而影响性能.尽管如此,范围几乎总是决定因素.
可以安全地补充说,可变大小的信息通常会堆在堆上.我所知道的唯一例外是C99中的VLA(支持有限)和alloca()函数,即使是C程序员也经常被误解.
@kai这是一种可视化的方式,但不一定是真实的.操作系统负责分配应用程序的堆栈和堆.编译器也负责,但主要依靠操作系统来执行此操作.堆栈是有限的,堆不是.这是由于操作系统处理将这些内存地址排序为更结构化的内容的方式,以便多个应用程序可以在同一系统上运行.堆和堆栈不是唯一的,但它们通常是大多数开发人员关心的唯一两个.

2> MarkR..:

我会说:

如果可以的话,将它存储在堆栈中.

如果需要,将其存储在堆上.

因此,更喜欢堆栈到堆.您无法在堆栈中存储某些内容的一些可能原因是:

它太大了 - 在32位操作系统上的多线程程序中,堆栈有一个小而固定的(至少在线程创建时)大小(通常只有几兆).这样你就可以创建大量线程而不会耗费地址对于64位程序或单线程(Linux无论如何)程序,这不是一个主要问题.在32位Linux下,单线程程序通常使用动态堆栈,这些堆栈可以保持增长直到它们到达堆顶部.

您需要在原始堆栈框架范围之外访问它 - 这才是真正的主要原因.

使用合理的编译器,可以在堆上分配非固定大小的对象(通常是在编译时未知大小的数组).


mrree:Nintendo DS堆栈是16千字节.一些堆栈限制取决于硬件.
这是我在开始时不会关注用户的事情.对于用户,即使STL将内容存储在堆上,也可以在堆栈上分配向量和列表.问题似乎更多的是决定何时明确调用new/delete.

3> Daniel Earwi..:

它比其他答案暗示的更微妙.根据您的声明方式,堆栈上的数据与堆上的数据之间没有绝对的区别.例如:

std::vector v(10);

在函数体中,它声明vector堆栈上有十个整数的(动态数组).但是由它管理的存储vector不在堆栈中.

啊,但是(其他答案提示)该存储的生命周期受其vector自身的生命周期的限制,这是基于堆栈的,所以它实现的方式没有区别 - 我们只能将其视为基于堆栈的对象具有价值语义.

不是这样.假设函数是:

void GetSomeNumbers(std::vector &result)
{
    std::vector v(10);

    // fill v with numbers

    result.swap(v);
}

因此,任何具有swap函数的函数(以及任何复杂值类型都应该具有一个函数)都可以作为一种对某些堆数据的可重新引用的引用,在一个保证该数据的单个所有者的系统下.

因此,现代C++方法永远不会将堆数据的地址存储在裸局部指针变量中.所有堆分配必须隐藏在类中.

如果你这样做,你可以把你程序中的所有变量看作是简单的值类型,并且完全忘记堆(除了为某些堆数据编写一个类似值的包装类时,这应该是不寻常的) .

您只需要保留一些特殊的知识来帮助您优化:在可能的情况下,而不是像这样将一个变量分配给另一个变量:

a = b;

像这样交换它们:

a.swap(b);

因为它更快,并且不会抛出异常.唯一的要求是你不需要b继续保持相同的值(它将获得相应a的值,这将被破坏a = b).

缺点是这种方法迫使您通过输出参数而不是实际返回值从函数返回值.但他们正在使用rvalue引用在C++ 0x中修复它.

在最复杂的情​​况下,你会把这个想法带到一般极端并使用智能指针类,例如shared_ptr已经在tr1中.(虽然我认为如果你似乎需要它,你可能已经超出了标准C++的适用性的最佳位置.)



4> 1800 INFORMA..:

如果需要在创建它的函数范围之外使用它,也可以在堆上存储项目.与堆栈对象一起使用的一个习惯称为RAII - 这涉及使用基于堆栈的对象作为资源的包装器,当对象被销毁时,资源将被清除.基于堆栈的对象更容易跟踪何时抛出异常 - 您不必担心在异常处理程序中删除基于堆的对象.这就是为什么原始指针通常不在现代C++中使用的原因,你可以使用一个智能指针,它可以是一个基于堆栈的包装器,用于指向基于堆的对象的原始指针.



5> Nick..:

要添加到其他答案,它也可以是性能,至少一点点.除非它与您相关,否则您不应该担心这一点,但是:

在堆中分配需要找到跟踪内存块,这不是一个恒定时间操作(并且需要一些周期和开销).随着内存碎片化,和/或您接近使用100%的地址空间,这可能会变慢.另一方面,堆栈分配是恒定时间,基本上是"免费"操作.

要考虑的另一件事(再次,如果它成为一个问题,真的唯一重要)是通常堆栈大小是固定的,并且可以远低于堆大小.因此,如果您要分配大对象或许多小对象,您可能希望使用堆; 如果你的堆栈空间不足,运行时将抛出网站名称异常.通常不是什么大不了的事,但需要考虑的另一件事.

推荐阅读
Gbom2402851125
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有