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

时间:2015-01-15 03:15:43

标签: c++ memory-management scope stack

Spoiler只包含背景和上下文

  

This post mentions,为了解决当对象超出范围而被解除分配的事实时,只需返回堆栈分配对象的对象,使其保持在范围内。这显然是在其他地方的堆栈上复制了对象。    This guy甚至确认你应该总是喜欢分配到堆栈。    在C ++中,使用类似的东西:

     

Object* my_object = new Object();

     

!然而,动态地将对象实例化到堆   !   Object my_object = Object();

     

!实例化堆栈中的对象。堆栈的大小有限,而堆实际上不是(物理限制除外)。但是,根据这个this post,堆栈访问时间要快得多,当然,当它超出范围时,释放是自动的。


我正在尝试创建一个速度绝对关键的应用程序,我不能只在main中实例化堆栈中的所有对象,只需将嵌套范围内的每个实例保存到外部容器中吗?

我使用包含属性“id”的简单Node类自己测试了这个。 我实例化了堆栈上的节点,将它们放在一个向量中,这样它们就不会被解除分配,然后(只是为了检查)我将新项目分配给堆栈,然后检查以确保先前分配的数据仍然存在。我可以继续在一个大规模的问题上实现这个吗?

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;
}

修改 见下面的评论。从创建的范围返回堆栈分配的对象时,将创建所述对象的副本。这个副本也在堆栈上吗?如果我将该复制的对象分配给main范围内声明的向量,该对象是否仍然在堆栈中?

2 个答案:

答案 0 :(得分:1)

  

我不能只在main中实例化堆栈中的所有对象吗?

如果您可以考虑所需的所有对象,

哎呀,COBOL将这种方法视为一种给定方式。 “以下是您需要的所有变量......”

  

我可以继续在一个大规模的问题上实现这个吗?

拥有无限的记忆,是的。总是

对于有限内存,您可能希望管理对象生命周期,并且只需要您实际需要的内存。

答案 1 :(得分:1)

在某些情况下,您可以为某些程序执行此操作。例如,在时间的深处,定义了Fortran,因此程序使用的所有数据都可以静态分配(并且通常是这样)。

与此同时,这是非常有限和有问题的。仅举几个例子,它排除了(几乎)所有递归,这对于处理某些类型的递归数据结构(例如树)非常方便。

这也意味着所有变量基本上都是全局变量,因此(例如)程序中的任何代码都可以读取和/或写入程序中的几乎任何变量。使用此模型的Fortran和(早期版本)BASIC等语言的经验表明,需要大量的纪律来开发目前被视为中小型程序的东西,并且开发现在通常被视为大型系统的东西可能是几乎不可能。代码的不同部分之间的依赖关系变得如此复杂以至于几乎不可能确定在哪里使用什么,哪些部分依赖于其他部分等等。

我怀疑这在实践中是否合理。分配堆栈空间的开销是所以小而开始,消除它只是不会以任何明显的程度提高速度。事实上,它可能很容易做到恰恰相反。预先分配变量意味着每个变量(几乎都是必需的)都存在于一个独特的内存部分中。其中相对较大比例的部分内存是在给定时间内当前不在缓存中的部分内存,因此最终会导致参考位置不佳导致缓存使用率不佳。

输入函数时分配本地数据意味着大多数变量都位于堆栈顶部或靠近堆栈顶部。由于您几乎不断地使用堆栈顶部附近的内存,因此大多数时间内该内存都会保留在缓存中,因此几乎所有内存访问都会访问缓存。

分配所花费的时间通常(好)低于1%,但访问主内存而不是缓存的代价通常至少为10倍,而且通常要高得多(20-50倍是很常见的)。您的里程将根据数据访问模式而有所不同,但您有巨大的潜在损失,并且(充其量)只有很小的机会获得微小的收益。

总结:这是一个糟糕的想法。它的很多更有可能造成很大的伤害,而不是一点点的好处。