C++ RAII not working?

2019-03-18 06:24发布

I'm just getting started with RAII in C++ and set up a little test case. Either my code is deeply confused, or RAII is not working! (I guess it is the former).

If I run:

#include <exception>
#include <iostream>
class A {
public:
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
private:
    int i_;
};

int main(void) {
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}

with the exception commented out I get:

A 1 constructed
A 2 constructed
A 2 destructed
A 1 destructed

as expected, but with the exception I get:

A 1 constructed
A 2 constructed
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
Aborted

so my objects aren't destructed even though they are going out of scope. Is this not the whole basis for RAII.

Pointers and corrections much appreciated!

10条回答
聊天终结者
2楼-- · 2019-03-18 07:07

The problem is that main has a special status. When an exception is thrown from there, the stack can't be meaningfully unwound, the application just calls std:terminate instead.

And then it makes a bit of sense why the variables don't go out of scope. We haven't actually left the scope in which they were declared. What happens could be considered to be equivalent to this:

int main(void) {
  A a1(1);
  A a2(2);
  std::terminate();
}

(I believe it is implementation-defined whether destructors are called in this case though, so on some platforms, it'll work as you expected)

查看更多
狗以群分
3楼-- · 2019-03-18 07:11

The following code works.

#include <exception> 
#include <iostream> 

class A { 
public: 
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; } 
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; } 
private: 
    int i_; 
}; 

void test() {
    A a1(1); 
    A a2(2); 
    throw std::exception(); 
} 

int main(void) { 
 try {
  test();
 } catch(...) {
 }
    return 0; 
} 
查看更多
做自己的国王
4楼-- · 2019-03-18 07:12

Others have suggested putting a try/catch inside main() to handle this, which works fine. For some reason I find the rarely used 'function-try-block' to look better, which surprises me (I thought it would look too weird). But I don't think there's any real advantage:

int main(void) 
try
{
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}
catch (...) 
{
    throw;
}

A couple of disadvantages are that since it's rarely used a lot of developers get thrown for a loop when they see it, and VC6 chokes on it if that's a consideration.

查看更多
我欲成王,谁敢阻挡
5楼-- · 2019-03-18 07:14

As others have pointed out, you've got an uncaught exception, which calls terminate(). It is implementation-defined (see the Standard, 15.3 paragraph 9 and 15.5.1 paragraph 2) whether destructors are called in this case, and the definition in your implementation is apparently "No, they won't". (If terminate() is called for any other reason than throwing an exception that doesn't have a handler, destructors will not be called.)

查看更多
登录 后发表回答