The*_* do 5 c++ initialization
我在之前的一个问题中提到过,我正在阅读Herb Sutter和Andrei Alexandrescu撰写的"C++编码标准"一书.在其中一章中他们说的是这样的:
始终在构造函数体中而不是在初始化列表中执行非托管资源获取,例如其结果未立即传递给智能指针构造函数的新表达式.
这是否意味着我应该使用这种形式的构造(假设data_3_必须用new初始化):
SomeClass(const T& value, const U& value2, const R& value3)
: data_(value), data_2_(value2)
{
data_3_ = new value3;
}
Run Code Online (Sandbox Code Playgroud)
代替:
SomeClass(const T& value, const U& value2, const R& value3)
: data_(value), data_2_(value2), data_3_(new value3)
// here data_3_ is initialized in ctor initialization list
// as far as I understand that incorrect way according to authors
{
}
Run Code Online (Sandbox Code Playgroud)
提前致谢.
PS如果这就是他们的意思,为什么他们使用术语非托管资源获取?我一直认为这些资源是"手动管理"的?
PS 2.我很抱歉,如果这篇文章中有任何格式问题 - 我不得不承认 - 我绝对讨厌在这个论坛上格式化的方式.
Mik*_*our 10
如果类包含两个或更多非托管资源,则必须提供建议.如果一个分配失败,那么您将需要释放所有先前分配的资源以避免泄漏.(编辑:更一般地说,分配资源后抛出的任何异常都必须通过删除该资源来处理).如果在初始化列表中分配它们,则无法执行此操作.例如:
SomeClass() : data1(new value1), data2(new value2) {}
Run Code Online (Sandbox Code Playgroud)
value1如果new value2抛出会泄漏.您将需要处理此问题,如下所示:
SomeClass() : data1(0), data2(0)
{
data1 = new value1; // could be in the initialiser list if you want
try
{
data2 = new value2;
}
catch (...)
{
delete data1;
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,通过合理使用智能指针可以避免所有这些恶作剧.
如果构造函数在任何阶段抛出异常,则手动管理资源的初始化可能会导致资源泄漏.
首先,考虑使用自动管理资源的此代码:
class Breakfast {
public:
Breakfast()
: spam(new Spam)
, sausage(new Sausage)
, eggs(new Eggs)
{}
~Breakfast() {}
private:
// Automatically managed resources.
boost::shared_ptr<Spam> spam;
boost::shared_ptr<Sausage> sausage;
boost::shared_ptr<Eggs> eggs;
};
Run Code Online (Sandbox Code Playgroud)
如果"new Eggs"抛出,~Breakfast则不会被调用,但是所有构造成员的析构函数都以相反的顺序调用,这是sausage和的析构函数spam.
所有资源都已正确发布,这里没问题.
如果您使用原始指针(手动管理):
class Breakfast {
public:
Breakfast()
: spam(new Spam)
, sausage(new Sausage)
, eggs(new Eggs)
{}
~Breakfast() {
delete eggs;
delete sausage;
delete spam;
}
private:
// Manually managed resources.
Spam *spam;
Sausage *sausage;
Eggs *eggs;
};
Run Code Online (Sandbox Code Playgroud)
如果"new Eggs"抛出,记住,~Breakfast不叫,但相当的析构函数spam和sausage(这是什么在这个事业,因为我们有原始指针为实际的对象).
因此你有泄漏.
重写上面代码的正确方法是:
class Breakfast {
public:
Breakfast()
: spam(NULL)
, sausage(NULL)
, eggs(NULL)
{
try {
spam = new Spam;
sausage = new Sausage;
eggs = new Eggs;
} catch (...) {
Cleanup();
throw;
}
}
~Breakfast() {
Cleanup();
}
private:
void Cleanup() {
// OK to delete NULL pointers.
delete eggs;
delete sausage;
delete spam;
}
// Manually managed resources.
Spam *spam;
Sausage *sausage;
Eggs *eggs;
};
Run Code Online (Sandbox Code Playgroud)
当然,您应该更喜欢将每个非托管资源包装在一个单独的RAII类中,这样您就可以自动管理它们并将它们组合到其他类中.
这是因为 SomeClass 的构造函数可能会抛出异常。
在您描述的情况下(即不使用智能指针),您必须释放析构函数中的资源,并且如果 SomeClass 的构造函数抛出异常,则使用 try-catch 块:
SomeClass(const T& value, const U& value2, const R& value3):data_(value),data_2_(value2) :
data_3_(NULL)
{
try
{
data_3_ = new value3;
// more code here that may throw an exception
}
catch(...)
{
delete data_3_;
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
.. 如果初始化列表中抛出异常,则无法执行此操作。
请参阅此以获得进一步的解释。