解决将对象传递给抛出的函数时可能出现的内存泄漏问题
Solve possible memory leak when passing an object to a function that throws
考虑函数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?...
调用程序无法捕获异常以删除指针,因为在引发异常之前,函数可能已成功将指针添加到数据结构中,释放对象会使数据结构不健全(悬空指针)。
当我开始阅读代码时,我在这一点上停止了:
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中的模拟)。
在提到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
结束时将发生删除(假设没有其他共享指针对资源有效)。
相关文章:
- C++ 中 std::vector 的内存问题
- 实现快速排序的内存问题
- 此add_node函数会导致内存问题吗?
- C++:为什么这段代码给我内存问题/未定义的行为?
- 我不明白附加字符串的内存问题
- 是否有可能存在不会崩溃程序的内存问题
- 在发布版本中删除类指针会导致内存问题
- Android OpenCV 应用程序的内存问题
- 线程的内存问题(微小线程,C++)
- 递归结构的向量有内存问题
- C++合并排序内存问题
- 将智能指针发送到Protobaf.内存问题
- 潜在的动态内存问题
- JNI 中的内存问题
- C++Lambda函数关闭-内存问题
- 解决由全局静态变量引起的内存问题
- 类堆栈内存问题(致命错误)
- QNetworkAccessManager内存问题
- 读取进程内存问题未更新
- 类指针内存问题