【问题标题】:Custom destructor in STL containerSTL 容器中的自定义析构函数
【发布时间】:2013-03-25 12:22:06
【问题描述】:

假设我有一个 STL 向量,其元素是指向其他类的原始指针。很明显,向量的析构函数不会释放这些指针所拥有的内存。是否可以实现一个自定义析构函数来释放这个内存?

【问题讨论】:

  • 你需要的不仅仅是一个析构函数(以及三法则的其余部分);您还需要非常小心,不要因重新分配元素而丢失指针。有关血腥细节,请参见 Boost 指针容器的实现;在现代 C++ 中,只需使用 vector<unique_ptr>。
  • @MikeSeymour 无论你做什么,都不要在容器中使用unique_ptr。迟早,你一定会做类似auto x = v[i]; 的事情,有效地从容器中删除指向对象。如果必须,请使用 shared_ptr,但 Boost 指针容器是更好的解决方案,在极少数情况下容器应该拥有对象。
  • @JamesKanze:您是否在考虑auto_ptr,(这些天)您不应该将其用于任何事情?使用唯一指针,auto x = v[i] 不应该在没有显式 move 的情况下编译。
  • @JamesKanze:我不知道你在说什么;我从未听说过与在容器中使用 unique_ptr 相关的任何“问题”,在我看来,这对它们来说是一个理想的用途。 auto x = std::move(v[i]) 将显式提取一个值; auto x = v[i] 不会编译。
  • @JamesKanze:unique_ptr 的论点是它完全符合 OP 的要求:通过容器中的指针自动管理对象的生命周期。是你试图根据甚至无法编译的示例代码所说明的“问题”的模糊讨论来反对它,以及它不能“正常工作”的模糊断言,无论这意味着什么。如果您有更好的解决方案,请写下答案。

标签: c++ stl


【解决方案1】:

在现代 C++ 中,使用 vector<unique_ptr<T>>,所有所有权问题都会为您管理。

如果 C++11 不可用,您可以使用 shared_ptr(来自 Boost 或 TR1)而不是 unique_ptr,或者您可以使用 Boost's pointer containers。 (不要尝试使用已弃用的auto_ptr,因为这样很容易意外地从容器中删除指针。大概第一条评论指的是这个,但将它与更安全的unique_ptr 混淆了。 )

如果由于某种原因你不能使用这些,或者如果你真的想自己做这些工作,你需要将向量包装在一个类中:

  • 用于删除每个存储的指针的析构函数;
  • 复制构造函数和复制赋值运算符,要么被删除,要么执行“深度”复制;否则,存在两个向量认为它们拥有相同对象的危险;
  • 访问器以这样一种方式读取和修改元素,这样您就无法在不删除其对象的情况下覆盖存储的指针。

【讨论】:

  • 使用unique_ptr 肯定会解决问题。它确实不在容器中工作得很好,因为像auto x = v[i]; 或auto x = *iter; 这样的东西会让你在容器中留下无效的unique_ptr。
  • @JamesKanze:不,它不会,因为它不会编译。 auto x = move(v[i]) 会将unique_ptr 移出容器;但如果您明确使用move,大概就是这样的意图。
  • @JamesKanze:除了玩具程序外,我并没有真正使用过 C++11 的特性,但我的理解是 std::unique_ptr 旨在解决这个特殊问题。是什么让这是一个糟糕(甚至是最糟糕)的解决方案?
  • @JamesKanze 我可以从调用方 WRT 看到的唯一限制是原始指针向量是您不能复制元素,因此从这个意义上说,它不能盲目地用作替代品。但是尝试这样做会给你一个编译器错误,我认为这是完全合理的。
  • @jbgs 我不确定效率,但无论您采用哪种解决方案,您都必须意识到它的局限性。恕我直言,当使用指针的唯一真正原因是多态性时,Boost 的指针容器是理想的解决方案,但是您仍然无法在不切片的情况下安全地复制返回的值。 shared_ptr 或多或少意味着读者也应该使用shared_ptr。而unique_ptr 或多或少意味着读者如果想保留“值”,就必须提取原始指针。
【解决方案2】:

没有。您应该在销毁矢量之前手动清除元素。类似的东西

std::for_each(v.begin(), v.end(), [](const T* p) { delete p; });

或者您可以使用 boost::ptr_vector(或一些 smart_pointers)之类的东西来处理这种情况。

【讨论】:

  • 如果你有 C++11,使用 std::unique_ptr 比手动使用 lambda 进行清理更干净。
  • @DavidRodríguez-dribeas 使用unique_ptr 是我能想到的最糟糕的解决方案之一。
  • @JamesKanze:你能详细说明一下吗?我的理解是unique_ptr 专门针对这种情况。
  • @MikeSeymour 我相信它的目的是让它在标准容器中可用。问题仍然是容器的整个设计都围绕着价值语义,结果是遵循这个想法的人最终会以一种或另一种方式(可能是无意的)复制容器的元素。并且复制unique_ptr 确实具有不寻常的语义。替代解决方案(Boost 的指针容器或 shared_ptr)更安全。
  • @JamesKanze:我仍然不同意你的反对意见。复制它们没有“不寻常的语义”;它们只是不可复制的,并且 C++11 容器已经过重新设计,专门用于处理不可复制的值类型。它们不能被无意复制,因为它们根本不能被复制。如果您的论点是“不了解移动语义的人可能会被移动语义混淆”,那么这是对的,但几乎没有理由避免它们。
【解决方案3】:

您需要在删除时遍历向量并调用其中每个元素的析构函数

或者我想最好的选择是,而不是保存 A*,而是保存 shared_ptr<A>,然后当没有人不再指向 A 时,它将被破坏

【讨论】:

  • 一个小警告:std::vector 没有虚拟析构函数。
猜你喜欢
  • 2016-11-05
  • 1970-01-01
  • 2013-05-23
  • 2010-12-11
  • 2019-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
相关资源
最近更新 更多