简体   繁体   English

为什么不总是实例化堆栈上的对象? C ++

[英]Why not always instantiate objects on the stack? C++

Spoiler just contains background and context 剧透只包含背景和上下文

! This post mentions that, to get around the fact that when object goes out of scope it is deallocated, simply return the object the stack-allocated object so it remains in scope. 这篇文章提到 ,要避免这样的事实,即当对象超出范围时,它会被释放,只需将对象返回堆栈分配的对象,使其保留在范围内即可。 This apparently makes a copy of the object on the stack somewhere else. 显然,这会在其他位置在堆栈上复制对象。 This guy even confirms that you should always prefer allocating to stack. 这个家伙甚至确认您应该始终喜欢分配到堆栈。 In C++, using something like: 在C ++中,使用类似以下内容的代码:

! Object* my_object = new Object();

! Dynamically instantiates an object to the heap, yet ! 动态地实例化一个对象到堆,但是! Object my_object = Object();

! Instantiates an object on the stack. 实例化堆栈上的对象。 The stack is limited in size whereas the heap is practically not (other than physical limits). 堆栈的大小受到限制,而堆实际上没有限制(物理限制除外)。 But also, according this this post , stack access time is much faster, and of course the deallocation is automatic when it goes out of scope. 而且,根据这篇文章 ,堆栈访问时间要快得多,并且当超出范围时,当然释放是自动的。


I'm trying to create an application where speed is absolutely critical, can't I just instantiate all of my objects on the stack within main, and simply save every instantiation that's within a nested scope to an outside container? 我正在尝试创建一个对速度绝对至关重要的应用程序,难道我不能只在main内实例化堆栈上的所有对象,而只是将嵌套范围内的每个实例保存到外部容器中吗?

I tested this myself using a simple Node class that contains the property "id". 我使用包含属性“ id”的简单Node类自己对此进行了测试。 I instantiated the nodes on the stack, put them in a vector so they don't get deallocated, then (just to check) I allocated new items to the stack and then checked to make sure the data to the previous allocations still existed. 我实例化了堆栈上的节点,将它们放在向量中,这样就不会释放它们,然后(只是检查)我将新项目分配到堆栈,然后检查以确保仍然存在先前分配的数据。 Can I continue to implement this on a somewhat large-scale problem? 我可以继续在一个规模较大的问题上实现这一点吗?

int main()
{
  vector<Node> stack_nodes;
  for (int i = 0; i < 2; ++i)
  {
    stack_nodes.push_back(Node(i)); // push newly copied stack-allocated objects so they don't die 
  }
  Node new_node1 = Node(3); // allocate two more to test stack memory overwriting 
  Node new_node2 = Node(4);
  cout << stack_nodes.at(1).getID(); // outputs 1! It's still there?
  return 0;
}

EDIT: See comments below. 编辑:请参阅下面的评论。 When you return the stack-allocated object from the scope it was created in, a copy of said object is created. 从创建它的作用域返回堆栈分配的对象时,将创建该对象的副本。 Is that copy also on the stack? 该副本是否也在堆栈上? If I assign that copied object to a vector declared in the scope of main, will that object still be on the stack? 如果我将复制的对象分配给在main范围内声明的向量,该对象是否仍在堆栈中?

can't I just instantiate all of my objects on the stack within main? 我不能只实例化main中堆栈上的所有对象吗?

If you can account for all the objects you'll need, yes . 如果您可以说明所有需要的对象,则可以

Heck, COBOL takes that approach as a given. 哎呀,COBOL采取了这种方法。 "Here is every variable you will ever need..." “这是您永远需要的每个变量……”

Can I continue to implement this on a somewhat large-scale problem? 我可以继续在一个规模较大的问题上实现这一点吗?

With infinite memory, yes. 有无限的内存,是的。 Always. 总是。

With finite memory, you may want to manage object lifetimes, and only demand the memory that you actually need at any time. 使用有限的内存,您可能需要管理对象的生存期,并且仅在任何时候都需要实际需要的内存。

You can certainly do this for some programs under some circumstances. 在某些情况下,您当然可以对某些程序执行此操作。 Just for example, back in the depths of time, Fortran was defined so all the data used by a program could be allocated statically (and it pretty routinely was). 举例来说,回想过去,Fortran的定义是为了使程序使用的所有数据可以静态分配(这是常规方式)。

At the same time, it is quite limiting and problematic. 同时,这是非常有限的并且有问题。 Just for a few examples, it rules out (almost) all recursion, which can be really handy for working with some types of recursive data structures (eg, trees). 仅举几个例子,它排除了(几乎)所有递归,这对于处理某些类型的递归数据结构(例如树)确实非常方便。

It also means that all your variables become essentially globals, so (for example) any code in the program can read and/or write almost any variable in the program. 这也意味着所有变量实际上都是全局变量,因此(例如)程序中的任何代码都可以读取和/或写入程序中的几乎任何变量。 Experience with languages like Fortran and (early versions of) BASIC that used this model indicates that it requires substantial discipline to develop what's currently seen as a small- to medium-sized program, and and developing what's now typically seen as a large system is probably next to impossible. 具有使用该模型的Fortran和BASIC(早期版本)等语言的经验表明,开发当前被视为中小型程序的程序需要大量的纪律,并且开发通常被视为大型系统的程序可能几乎不可能。 Dependencies between different parts of the code become so complex so quickly that it becomes nearly impossible to determine what's used where, what parts depend upon what others, etc. 代码不同部分之间的依赖关系变得如此复杂,以至于几乎不可能确定在哪里使用了什么,哪些部分取决于其他部分等等。

I doubt this can be justified in practice. 我怀疑这在实践中是否合理。 The overhead of allocating stack space starts out so minuscule that eliminating it simply won't improve speed by any noticeable degree. 分配堆栈空间的开销开出如此微不足道是消除它根本不会受到任何显着的程度的提高速度。 In fact, it may easily do exactly the opposite. 实际上,它可能很容易做到相反。 Pre-allocating your variables means that each will (pretty much of necessity) live in a unique part of memory. 预分配变量意味着(必不可少的)每个变量都将驻留在内存的唯一部分中。 A relatively large percentage of them will be to parts of memory that aren't currently in the cache at a given time, so you'll end up with poor locality of reference leading to poor cache usage. 在给定的时间中,它们中的很大一部分将用于当前不在缓存中的内存部分,因此您最终会遇到较差的引用局部性,从而导致较差的缓存使用率。

Allocating local data when you enter a function means that most of your variables live at or close to the top of the stack. 输入函数时分配本地数据意味着大多数变量都位于栈顶或栈顶附近。 Since you're using the memory near the top of the stack almost constantly, that memory stays in the cache most of the time, so nearly all your memory accesses hit the cache. 由于您几乎一直在使用靠近堆栈顶部的内存,因此该内存大部分时间都保留在缓存中,因此几乎所有的内存访问都命中了缓存。

The time taken for allocations is typically (well) under 1%, but the penalty for accessing main memory instead of the cache is typically at least 10x, and often much higher (20-50x is pretty common). 分配时间通常(远远)低于1%,但是访问主内存而不是高速缓存的代价通常至少是10倍,并且通常要高得多(通常20到50倍)。 Your mileage will vary depending on data access patterns, but you have a huge potential for a large loss, and (at best) only a small chance of a tiny gain. 根据数据访问方式的不同,里程数会有所不同,但是您有巨大的潜在巨大损失,(最多)只有很小的机会获得微不足道的收益。

Summary: this is a lousy idea. 简介:这是一个糟糕的想法。 It's much more likely to do a lot of harm than even a tiny bit of good. 容易做了很多的伤害比好,甚至一点点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM