【问题标题】:Why is std::string/std::map not encouraged to be created on the heap?为什么不鼓励在堆上创建 std::string/std::map?
【发布时间】:2023-03-13 05:46:01
【问题描述】:

我注意到多个问题,C++ 专家询问 std::string/std::map/etc。不应使用“new”关键字创建(C++ 新手,如果不明显的话)。

所以,如果我的理解是正确的,这不会在堆上创建它,而是在堆栈上创建。这意味着一旦函数超出范围,对象就会消失,但我认为事实并非如此,我的理解是错误的。

这是因为底层模板在堆上实例化它并使用 auto_ptr 对其进行管理,从而不会导致内存泄漏?这适用于所有 stl 类吗?

另外,一个后续问题是创建插入地图的对象的方法应该是什么?是否应该在堆上分配它们(如果它们在函数范围之外有价值)?

编辑:

我确实了解堆和堆栈之间的区别以及使用它们的原因(我可能不清楚这一点)。

我要求这样做的原因是,仅在堆栈上为我想保留的对象实例化一个对象似乎不自然。但是,我想这就是语法的样子。

这意味着,当我写作时,我觉得我在堆栈上有些东西,

std::map<int,int> mymap;

而不是,

std::map<int,int> *mymap = new std::map<int,int>;

我也想知道这对内存的影响。既然现在这个实现自己清理了内存,是不是类似于Java中的垃圾回收?使用 stl 对象是否会对性能产生隐含影响?

【问题讨论】:

  • 您输入的内容被复制了。不过,我不知道您对auto_ptr 有何看法。我怀疑任何实现在任何容器的实现中都使用它。在堆栈上创建东西的好处是您不必管理它的内存,因为当对象超出范围时它会被释放。它也更加安全。
  • “一旦函数超出范围,对象就会消失”当你关心这个问题时,你不应该首先在堆上创建。
  • 也许一个带有预期问题或流程的特定代码示例可以帮助我们准确了解我们中的哪一部分可能与不同的条款不同。
  • 只有在方法返回后需要对象持久化时才需要使用new。它不是特定于字符串或映射或任何东西,它是一般规则。
  • 如果你有机会,看看 Scott Meyers 的 Effective C++。那里有一章是关于使用智能指针来防止内存泄漏的。它专门讨论了下面一张海报提到的“RAII”成语。一个简单的例子是使用new 来初始化一些东西,在处理它的中途,一些东西引发了一个异常,导致程序控制永远无法到达宝贵的delete 语句,该语句将正确地取消分配内存。有你的内存泄漏:) 有了智能指针,就不用担心了。

标签: c++ memory-management stl


【解决方案1】:

这不是 std::stringstd::map 所特有的。这只是关于 C++ 中几乎所有对象的一般规则。

为了自动化资源管理,您通常希望将每个资源绑定到某个范围,该范围包含需要该资源的时间。假设您始终如一地这样做,当执行离开定义它的范围时,每个资源都会自动清理。

这通常被称为“RAII”(资源获取即初始化),尽管有些人使用更具描述性的术语 SBRM(范围绑定资源管理)。无论您使用何种术语,只要使用得当(且始终如一),它都能很好地发挥作用。

“RAII”一词的原因是,这意味着大多数资源是在对象构建期间获取的,并在对象销毁期间释放。这往往会导致一种相当特定的编码风格,其中(除其他外)对象非常精细,因为每个对象都管理一个特定资源的生命周期。

【讨论】:

  • Ummmm...SBRM 是 Scope Bound 资源管理
  • @CaptainObvlious:糟糕。已更正。谢谢。
  • 当你说自动化资源管理时。这不是性能打击吗?我问这个是因为我觉得它一定像 Java 中的垃圾收集。
  • @dev_nut:这只是你离开作用域时对变量的正常破坏。除非您正在清理 大量 资源,否则时间通常是无关紧要的。它不像 GC 那样花费大量时间来找出仍在使用的变量。
  • @dev_nut:不——地图中的项目在/如果您移除它们时会被销毁。当映射超出范围时(如果它是局部变量,例如在某些函数中)或当您显式销毁它(如果您动态分配它)时,映射本身会被销毁。就长期存在而言,这通常只是找到合适的功能的问题。在极端情况下,它贯穿程序的整个生命周期,因此它可能定义在main 的开头。
【解决方案2】:

将某些东西放在堆上的一个常见原因是在编译时不知道大小,您需要灵活地分配一些未知数量的对象。 stringmap 的内部实现以及其他容器将根据需要自动从堆中分配,从而减轻您的负担。将对象本身放在堆上就变得多余了。

将容器放在堆上的唯一原因是当您需要它的生命周期比创建它的块长时。

【讨论】:

  • +1。特别是对于“将容器放在堆上的唯一原因是当您需要它的生命周期比创建它的块更长时。” - 这些“从不/总是”规则中的另一个尘埃落定。完全正确。
  • 即使生命周期比创建它的块长,使用 NRVO,您通常不需要将容器放在堆上,因为按值返回将避免所有开销(直接构造成调用者提供的空间,通常在他们自己的堆栈框架上)。
猜你喜欢
  • 2021-10-27
  • 2018-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多