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++;例外安全 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运算符,该对象的构造函数引发:

它说,如果其中一个T构造函数抛出,那么所有完全构造的T对象都会被正确地销毁,最后,会自动调用运算符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