C++ 当RAII对象无法构造时

C++ 当RAII对象无法构造时,c++,raii,C++,Raii,假设我构造了一个RAII对象,而该对象可能无法构造。我该怎么处理 try { std::vector<int> v(LOTS); // try scope ends here because that's what the catch is for } catch( const std::bad_alloc& ) { // ... } // v? what v? 试试看{ 标准:向量v(批次); //try scope在这里结束,因为这就是捕获的目的

假设我构造了一个RAII对象,而该对象可能无法构造。我该怎么处理

try {
    std::vector<int> v(LOTS);
    // try scope ends here because that's what the catch is for
} catch( const std::bad_alloc& ) {
    // ...
}
// v? what v?
试试看{
标准:向量v(批次);
//try scope在这里结束,因为这就是捕获的目的
}捕获(const std::bad_alloc&){
// ...
}
//v?什么v?
当然,
std::vector
的默认构造函数不会抛出,这会有所帮助,但这不是一般情况。构造函数很可能抛出。如果我想处理任何资源获取失败,在它没有抛出的情况下,我如何在仍然能够继续进行的情况下处理


编辑:澄清一下,我的问题是,如果资源无法获取,那么我可能想再试一次,以此类推。也许我可以尝试获取一个替代资源。

如果RAII构造函数抛出,那么在抛出点之前绑定到RAII对象的所有资源都将被正确清理。C++规则是合理设计的,保证了

如果您的
v
构造由于
错误分配而抛出,则在
try
块中
v
之前创建的任何RAII对象都将被正确清理

因此,如果您因此使用RAII,您不需要手动
try
/
catch
,因为RAII对象为您处理清理。如果您确实出于某种原因需要它,在上面的情况下,您可以像下面那样使用swap

std::vector<int> v;
try {
    std::vector<int> vtry(LOTS);
    v.swap(vtry); // no-throw
} catch( const std::bad_alloc& ) {
    // ...
}
// v!
std::vector v;
试一试{
std::矢量vtry(批次);
v、 交换(vtry);//不抛出
}捕获(const std::bad_alloc&){
// ...
}
//v!
取决于“继续”的含义。任何需要资源的操作都会失败:这就是“requires”的意思。因此,当您希望在发生错误后继续时,可能会编写如下代码:

void something_using_RAII(thingummy &t) {
    vector<int> v(t.size_required);
    // do something using v
}

...

for each thingummy {
    try {
         something_using_RAII(this_thingummy);
    } catch(const std::bad_alloc &) {
         std::cerr << "can't manage that one, sorry\n";
    }
}
string flag;
try
{
    flag = "creating vector<int> v";
    std::vector<int> v(LOTS);

    flag = "performing blaggity bloop";
    blaggity_bloop();

    flag = "doing some other stuff";
    some_other_stuff();
}
catch( const std::bad_alloc& )
{
    cerr << "Bad allocation while " << flag << endl;
}

如果“usev”和“usew”基本上是相同的代码,那么重构成一个函数并从两个地方调用它。您的函数在这一点上做了很多工作。

如果无法创建
v
,则所有尝试使用
v
的代码都无法执行。在代码使用的代码
v
之后移动
catch
,如果没有
v
,所有使用v的代码都需要在try块中,则可以继续执行。如果问题是如何缩小引发异常的代码范围,则可以使用某种标志指示您在try块中的位置,如下所示:

void something_using_RAII(thingummy &t) {
    vector<int> v(t.size_required);
    // do something using v
}

...

for each thingummy {
    try {
         something_using_RAII(this_thingummy);
    } catch(const std::bad_alloc &) {
         std::cerr << "can't manage that one, sorry\n";
    }
}
string flag;
try
{
    flag = "creating vector<int> v";
    std::vector<int> v(LOTS);

    flag = "performing blaggity bloop";
    blaggity_bloop();

    flag = "doing some other stuff";
    some_other_stuff();
}
catch( const std::bad_alloc& )
{
    cerr << "Bad allocation while " << flag << endl;
}
字符串标志;
尝试
{
flag=“创建向量v”;
标准:向量v(批次);
flag=“执行blaggity bloop”;
blaggity_bloop();
flag=“做其他事情”;
一些其他的东西;
}
捕获(const std::bad_alloc&)
{

cerr不完全确定问题是什么。使用v的代码必须在try块中。据我所知,问题是为了能够从
v
的构造函数中的异常中恢复,
v
必须在
try
范围内声明,这意味着它在
捕获后不再可见ode>block。因此,如果您有代码,一方面需要能够“忽略”在构造
v
时出现异常,另一方面,如果构造成功,则能够使用
v
如果一个资源无法获取,那么可能我可以使用另一个资源,而不是让异常传播。如果第二个资源失败,则可能我有另一个想法,因此n、 我觉得无论我采取什么方法,这都会导致代码混乱。RAII只是描述了在出现故障时如何清理,而不是如何解决故障。据我所知,问题是,在捕获中,您无法区分由v的ctor或try块中的其他代码引发的异常。@Roger:在这种情况下(除了litb的解决方案,这很好,但是依赖于
swap
,这不是所有RAII类都有)您必须编写一些恼人的东西,比如
try{vector v(LOTS);bool flag=true;try{use v;flag=false;}catch(…){X}}catch(…){Y}
.X处理来自try块中其他代码的异常,Y处理来自
v
的构造函数或析构函数的异常,以及来自X的异常。但是
v
的析构函数不应该抛出,并且来自X的异常可以通过标志识别。这不是关于清理。而是当RAII对象无法初始化。如果我什么都不做,那么异常会传播。不好。如果我的函数必须提供不抛出保证怎么办?如果RAII对象没有不抛出的构造函数怎么办?你可能会说没有不抛出的构造函数的对象设计得很差。没关系。我只是从来没有在任何地方看到过这个建议,所以如果这是真的,我想让它浮出水面。否则,听听你如何处理任何ctor都可以抛出的情况。@wilhelmtell,“如果RAII对象没有一个不会抛出的构造函数怎么办?”->我认为您需要将整个代码放在
try
子句中。我不认为没有无抛出构造函数是不好的设计。有时它可能不适合。但我发现如果存在这种无抛出状态,它会很有用。您还可以将对象包装成具有无抛出默认状态的智能指针(比如<代码> SyddypTr> /Cord>)。但是我认为这很恶心。如果C++正确地使用了表,那么块的执行成本就等于零。