c++;例外安全 Excel C++提到以下代码 template <class T> class Stack { public: Stack(); ~Stack(); /*...*/ private: T* v_; // ptr to a memory area big size_t vsize_; // enough for 'vsize_' T's size_t vused_; // # of T's actually in use }; template<class T> Stack<T>::Stack() : v_(new T[10]), // default allocation vsize_(10), vused_(0) // nothing used yet { } 模板类堆栈 { 公众: 堆栈(); ~Stack(); /*...*/ 私人: T*v_;//ptr到一个大内存区域 size\u t vsize\u;//足够“vsize\ut”使用 实际使用的t的大小 }; 模板 Stack::Stack() :v_uT(新的[10]),//默认分配 vsize_u10, vused_u0)//尚未使用任何内容 { }
它说,如果其中一个T构造函数抛出,那么所有完全构造的T对象都会被正确地销毁,最后,会自动调用运算符delete来释放内存。这使我们防漏 我的理解是,如果构造函数抛出异常,应用程序应该清理任何分配的资源。上面的防漏性能如何?[更正:]不是。构造函数中的异常不会泄漏资源,因为异常发生的唯一位置是c++;例外安全 Excel C++提到以下代码 template <class T> class Stack { public: Stack(); ~Stack(); /*...*/ private: T* v_; // ptr to a memory area big size_t vsize_; // enough for 'vsize_' T's size_t vused_; // # of T's actually in use }; template<class T> Stack<T>::Stack() : v_(new T[10]), // default allocation vsize_(10), vused_(0) // nothing used yet { } 模板类堆栈 { 公众: 堆栈(); ~Stack(); /*...*/ 私人: T*v_;//ptr到一个大内存区域 size\u t vsize\u;//足够“vsize\ut”使用 实际使用的t的大小 }; 模板 Stack::Stack() :v_uT(新的[10]),//默认分配 vsize_u10, vused_u0)//尚未使用任何内容 { },c++,exception,C++,Exception,它说,如果其中一个T构造函数抛出,那么所有完全构造的T对象都会被正确地销毁,最后,会自动调用运算符delete来释放内存。这使我们防漏 我的理解是,如果构造函数抛出异常,应用程序应该清理任何分配的资源。上面的防漏性能如何?[更正:]不是。构造函数中的异常不会泄漏资源,因为异常发生的唯一位置是new表达式内部,如果new表达式失败,则会释放它分配的资源。您的情况很特殊,因为您只在构造函数中进行了一次分配——通常这是不安全的 您引用的短语引用的是失败对象的delete运算符,该对象的构造函数引发:
new
表达式内部,如果new
表达式失败,则会释放它分配的资源。您的情况很特殊,因为您只在构造函数中进行了一次分配——通常这是不安全的
您引用的短语引用的是失败对象的delete运算符,该对象的构造函数引发:
struct T
{
T() { throw 1; }
char data[200];
};
// in your code:
T * pt = new T;
在最后一行中,在调用构造函数之前分配内存。在发生异常时,通过自动调用::operator delete(pt)
释放该内存。(通常,会调用与新表达式匹配的删除运算符(而不是“表达式”!)
事情是这样的:
- 成功建设:1。分配。2.建设3.毁灭。4.解除分配
- 施工不成功:1。分配。2.解除分配
new
,而是将其放在正文中:
class Danger
{
T * pt1, * pt2;
public:
Danger()
{
try { pt1 = new T; } catch(...) { throw(); }
try { pt2 = new T; } catch(...) { delete pt1; throw(); }
}
};
或者,根据单一责任的原则,不要使用原始指针,而是使用资源管理容器,这些容器会自行清理 引用C++03标准,§5.3.4/8: 新表达式通过调用分配函数来获取对象的存储。如果新表达式通过抛出异常终止,它可能通过调用释放函数释放存储。如果分配的类型是非数组类型,则分配函数的名称为
operator new
,解除分配函数的名称为operator delete
。如果分配的类型是数组类型,则分配函数的名称为operator new[]
,解除分配函数的名称为operator delete[]
§5.3.4/17:
如果上述对象初始化的任何部分通过抛出异常而终止,并且可以找到合适的释放函数,则调用释放函数以释放正在构造对象的内存,然后异常继续在新表达式的上下文中传播
因此,如果任何
T
构造函数抛出异常,运行时将销毁构造函数抛出的T
实例的任何已创建子对象,然后调用整个数组上的操作符delete[]
,正在销毁任何已创建的元素并取消分配数组内存。请尝试自动指针查找T*v
或任何动态分配的资源。如果发生以下情况
template<class T>
Stack<T>::Stack()
: v_(new T[10]),
vsize_(10),
vused_(0)
{
throw 0; // terminated by exception
}
模板
Stack::Stack()
:v_uT(新的[10]),
vsize_u10,
vused_(0)
{
抛出0;//异常终止
}
或者,
堆栈中有另一个对象
在构造时抛出异常,v
将导致内存泄漏。如果你把它包装成std::unique\u ptr v\ucode>或类似的东西,如果堆栈的构造被异常终止,它将自动被释放。我完全不同意,但我不想投反对票,因为你通常是个相当聪明的家伙。根据我在回答中引用的standardese,你真的认为OP的代码会泄漏吗?@Kerrek:更重要的是,OP中的代码绝对是好的。你的观点是,任何进一步的非POD子对象的构造函数都可能抛出,导致泄漏,但问题(正如我所读的)是关于该代码的,所以我认为你的回答不应该暗示它不是防泄漏的。@Kerrek:如果你读过这本书,可以理解,/*…*/
只是成员函数的声明。如图所示的代码列表包括所有数据成员和完整的构造函数。你已经在向唱诗班宣讲它不是一个好的设计(OP说他已经考虑了很多),但是OP问为什么书中说代码是防泄漏的,所以说它不是。。错,“潜在不安全”是什么意思?要么安全,要么不安全。这个密码是安全的。如果存在任何不安全性,它只能在T中,堆栈类不能预期也不能解释这一点。而且一次分配当然并不罕见,否则的做法是不好的。哦,不!删除该函数try block,以免在new抛出时使某人的程序崩溃。这让你想知道实现的效果如何:如果v_[5]
的构造函数抛出,那么Delete v_[]
只会destro