【问题标题】:C++ Memory Management Techniques/PracticesC++ 内存管理技术/实践
【发布时间】:2012-05-29 03:55:53
【问题描述】:

我已经用 C++ 启动了一个项目。这种语言的内存管理对我来说是新的。

我过去常常用new () 创建对象,然后传递指针,虽然它起作用了,但调试起来很痛苦,人们看到代码时都觉得我很有趣。我很自豪它没有泄漏或段错误(一旦修复),但这确实需要付出很多努力。

list <struct Connection *> users;

struct Connection * accept_connection (const char *name) {
  struct Connection * new_node = new Connection ();
  new_node->_data = ... // whatever, set it up here
  return new_node;
}

struct Connection * new_user = accept_connection (const char *name);
users.append (new_user);

因此,我为该项目的下一个版本制定了新策略。到目前为止,我正在做的是使用new () 创建对象并为它们分配一个唯一的整数 ID 号。然后,我使用 ID 作为键将对象存储在哈希表中。现在项目通过整数 ID 号存储和传递,当您确实需要取消引用它时,您转到哈希表,它会返回 thing *NULL。因此,我不再遇到指针错误,但代码速度有所降低。

typedef unsigned long ID_Number;

// create a user and return the ID
ID_Number create_user () {
  ID_Number new_id = assign_unique_id ();
  struct User * node = new User ();
  node->_id = new_id;
  node->_data = ... // whatever, set it up here
  add_to_users_dict (new_id, node);
  return new_id;
}

list <ID_Number> users;

for_each (users.begin(), users.end(), process_user);

void process_user (ID_Number i) {
  struct User * u_ptr = lookup_user_dict (i);
  if (u_ptr == NULL) {
    // not found in dict
    // somehow this guy was deleted
  } else {
    // we can do stuff with this guy
  }
}

现在我有点熟悉编程的基本原则,但作为一个自学成才的爱好者,我不熟悉行业实践和工具。 我基本上要求的是内存管理指南:

1) 我做对了还是错了?

2) 有什么可以帮助我的包或库吗?

3) 行业的标准做法是什么?

4) 基本上我应该在谷歌上搜索或购买 Kindle 等什么?

今天我通常使用 Python,它确实为我处理了很多“后端”的东西,但我需要 C 或 C++(我想我使用的是纯 C 加上 stdc++ 库,我不太确定语言之间的重叠是 - 我只知道 g++ 可以很好地编译它)对于这个特定项目的速度/性能原因:虽然我怀疑一些数学天才可以提供算法修复来加速它,尽管这是一个单独的问题。

【问题讨论】:

标签: c++ memory-management


【解决方案1】:

我能给出的最佳答案是,您不应该以传统方式使用指针。 C++11 改变了程序员处理内存管理的方式。

我将提供一些链接,而不是解释比我聪明得多的人已经详细解释过的事情。

首先你应该看的是 Herb Sutter 的文章Elements of Modern C++ Style 然后查看 Bjarne Stroustrup C++11 Style 的视频

如果您能够使用新的 C++11 标准,那么它使内存管理比以前更干净。

【讨论】:

  • 如果你不能使用 C++11(打电话给你的猎头公司!),有很多东西可以使用 C++03 来手动管理内存。
【解决方案2】:

您的第一个错误是使用new

很少需要动态内存,甚至比“直接”需要的还要少:大多数动态分配的对象都存在于容器中(如vectormap)。

您的第二个错误是不使用构造函数,一旦您了解什么是类不变量以及构造函数如何启用它们,那么您将能够利用 RAII(资源获取即初始化)并停止使用 C 编码。

【讨论】:

  • 我开始研究 RAII - 包装 ctor/dtor,以便在从堆栈中删除包装器时自动调用 dtor。那么使用 RAII 如何将一个事物的单个实例放入两个不同的列表中?我必须使用参考?那么如何阻止列表 B 遍历列表 A 说要删除的内容?看来我必须在每个结构中添加一个“is_deleted_skip_me”布尔值(并定期剔除它们),或者我需要在任何单个列表更改时继续清理我的列表? -- 编辑:如果应用程序需要这么多单独的列表,我开始认为应用程序设计不佳。
  • @gecko:许多相互依赖的列表看起来很糟糕,尽管我不知道你的限制。你能不复制信息,让每个列表都有自己的副本吗?否则,总是有可能使用shared_ptr,但它确实为未来的一些困难打开了大门。
  • 容器内的盒子(数据对象)(本身是同一结构的不同实例 - 仅名称和容量不同),因此容器具有其内容的列表。代理会记住它接触过的最后 N 个项目。因此,如果一个代理将盒子放入一个容器中,那就是 2 个包含相同项目的列表。
  • @gecko:我可以看到两种方法。第一个是几乎共享的所有权(shared_ptr,代理接受weak_ptr),第二个是基于通知的机制。您可以让代理在一个盒子上成为Observer(这是一种设计模式),并让盒子通知其观察者它即将被销毁,以便他们在它被有效销毁之前取消注册它。这就是异步和同步销毁检测的区别,取舍各不相同。
【解决方案3】:

我做对了还是错了?

您实际上创建了一个使用句柄而不是直接指针来引用对象的系统。这可能适用于某些场景。当操作系统“拥有”对象并管理其生命周期但允许客户端引用它时,操作系统通常会使用句柄。

是否有任何可以帮助我的包或库?

现代 C++ 中的标准库有 shared_ptr 和 unique_ptr,它们是管理动态对象生命周期的智能指针。它们是使用 RAII 的好方法。

行业的标准做法是什么?

C++ 中的事实标准是 RAII——资源分配是初始化。 RAII 将分配与构造函数联系起来,将分配与析构函数联系起来。由于 C++ 对 c'tors 和 d'tors 的执行方式和时间有可靠的保证,这为您提供了一种完美的方法来管理对象生命周期而不会泄漏。智能指针 shared_ptr 和 unique_ptr 也明确了对象的所有权。

基本上我应该在谷歌上搜索或购买 Kindle 等什么?

搜索 RAII。

【讨论】:

    【解决方案4】:

    1) 我做对了还是错了?

    您没有使用资源获取即初始化 (RAII) 习语或现代 C++ 所有权语义。

    2) 有什么可以帮助我的包或库吗?

    如果你真的需要传递指针,你可以使用 std::unique_ptr 和 std::shared_ptr。但在开始之前,您应该了解如何使您自己的对象表现为具有 RAII 语义的资源所有者。

    3) 行业的标准做法是什么?

    4) 基本上我应该在谷歌上搜索或购买 Kindle 等什么?

    RAII

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-25
      • 1970-01-01
      • 1970-01-01
      • 2013-11-19
      • 2012-07-31
      • 2013-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多