根据GOTW #56,以下代码中存在潜在的经典内存泄漏和异常安全问题:
// In some header file:
void f( T1*, T2* );
// In some implementation file:
f( new T1, new T2 );
原因是当 wenew T1
或时new T2
,类的构造函数可能会抛出异常。
同时,根据解释:
简要回顾:简单地说,像“new T1”这样的表达式称为 new-expression。回想一下 new-expression 的真正作用(为简单起见,我将忽略放置和数组形式,因为它们在这里不是很相关):
它分配内存
它在该内存中构造一个新对象
如果构造因异常而失败,则释放分配的内存
因此,每个 new 表达式本质上是一系列两个函数调用:一个对 operator new() 的调用(要么是全局的,要么是由正在创建的对象的类型提供的),然后是对构造函数的调用。
对于示例 1,考虑如果编译器决定生成如下代码会发生什么:
1:为 T1 分配内存
2:构造 T1
3:为 T2 分配内存
4:构造 T2
5:调用 f()问题是这样的:如果第 3 步或第 4 步由于异常而失败,则 C++ 标准不要求销毁 T1 对象并释放其内存。这是典型的内存泄漏,显然不是一件好事。[...]
通过阅读更多:
为什么标准不通过要求编译器在清理时做正确的事情来防止问题?
基本的答案是它没有被注意到,即使现在它已经被注意到,也可能不需要修复它。C++ 标准允许编译器对表达式的求值顺序有一定的自由度,因为这允许编译器执行原本不可能的优化。为实现这一点,表达式评估规则以非异常安全的方式指定,因此如果您想编写异常安全代码,您需要了解并避免这些情况。(请参阅下文了解如何最好地做到这一点。)
所以我的问题是:
如何修复这个典型的异常不安全代码?我们应该避免编写这样的代码吗?
答案让我有点困惑,为了处理构造函数失败,我们应该根据C++ FAQ从构造函数中抛出异常并确保分配的内存被正确释放,所以假设类 T 确实实现了处理构造失败的代码,我们是否上面的代码中还有异常安全问题吗?
感谢您的时间和帮助。