3

考虑函数 foo(MyClass* mc) 应该将 mc 的副本保留到内部数据结构中的情况,并保证在不再使用时将删除该对象。

void foo(MyClass* mc) // acquires ownership of mc; may throw
{
  // code that may throw
  bar(mc); // acquires mc; may also throw
}

当此函数执行可能抛出的代码(例如,OutOfMemory 异常)时,就会出现问题。如果在将指针保存到数据结构之前引发异常,则显然应该在函数展开之前释放对象,因为调用者不再对它负责(调用者甚至不知道指针是否实际保存在数据结构与否)。

可以使用 RAII 和范围保护来处理这个问题,但它看起来很笨拙,并且会产生一些开销(必须在每个获取指针的函数中完成)。

每次获取动态分配的对象时是否真的需要这样做,还是有一种更简洁的方法来做到这一点?!

template <class T>
struct VerifyAcq {
  T* ptr;
  bool done;
  VerifyAcq(T* ptr):ptr(ptr) { done = false; }
  ~VerifyAcq() {
    if (!done) delete ptr;
  }
};

void foo(MyClass* mc) // acquires mc; may throw
{
  VerifyAcq<MyClass> va(mc);
  // code that may throw
  bar(mc); // acquires mc; may throw; must implement the same mechanism!
  va.done = true;
}
// Note: there might be no public way of "undoing" what bar has done (no rollbak)
// and even if there was, what if it could also throw?...

调用者不能为了删除指针而捕获异常,因为在抛出异常之前,函数可能已经成功地将指针添加到数据结构中,释放对象会使数据结构变得不健全(悬空指针)。

4

2 回答 2

4

当我开始阅读代码时,我停在了这一点上:

void foo(MyClass* mc) // acquires ownership of mc; may throw

评论不是记录所有权收购的最佳方式。最好的方法是使用类型系统。

 void foo(std::unique_ptr<MyClass> mc) // no comment required

修复接口也最终解决了问题。

(如果您的标准库中没有 unique_ptr,还有其他替代方法,例如Howard Hinnant 在 C++03 中的仿真)。

于 2012-12-06T13:28:06.937 回答
1

提到 RAII 时,您的想法是正确的。

在您提到的场景中,您指出删除应该只在bar函数以某种方式无法存储指针时发生。像这样的共享指针boost::shared_ptr非常适合:

void foo(boost::shared_ptr<MyClass> mc)
{
    // code that may throw
    bar(mc); // acquires mc; may also throw
}

void bar(boost::shared_ptr<MyClass> mc)
{
    // code that may store the shared_ptr somewhere
}

只要资源(指针)至少有一个共享指针处于活动状态,它就不会被删除。一旦最后一个共享指针被销毁或重置,资源就会被删除。

例如:如果函数存储了共享指针,那么函数结束bar时不会发生删除。foo或者,如果bar函数未能存储共享指针,则在foo结束时将发生删除(假设没有其他共享指针对资源处于活动状态)。

于 2012-12-06T13:28:12.707 回答