John
John

Reputation: 131

C++ RAII not working?

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!

Upvotes: 13

Views: 1619

Answers (10)

ggg
ggg

Reputation: 1

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; 
} 

Upvotes: 0

Michael Burr
Michael Burr

Reputation: 340218

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.

Upvotes: 1

Alex
Alex

Reputation: 6933

You are not handling the exception properly, so your application is exiting before the objects go out of scope.

I am going to explain a little more. If an exception "bubbles" up to main the stack is unwound (edit). Even moving the code to a secondary function will not fix this issue. ex:

      1 #include <exception>
      2 #include <iostream>
      3
      4 void test();
      5
      6 class A {
      7     public:
      8         A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
      9         ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
     10     private:    int i_;
     11 };
     12
     13
     14 int main(void) {
     15     test();
     16     return 0;
     17 }
     18
     19 void test(){
     20             A a1(1);
     21             A a2(2);
     22            throw std::exception();
     23 }

The above code will not solve the issue. The only way to solve this is to wrap the thrown exception in a try-catch block. This will keep the exception from reaching the main, and stop termination that is happening before the objects go out of scope.

Upvotes: 3

jkerian
jkerian

Reputation: 17016

Since the exception is not handled by the time it reaches main(), it results in a call to std::terminate(), you essentially have the equivalent of

int main(void) {
  A a1(1);
  A a2(2);
  exit(1);
}

Destructors are NOT guaranteed to be called in cases where the program terminates before they go out of scope. For another hole in RAII, consider:

int main(void) {
  A *a1 = new A(1);
}

Upvotes: 0

Loki Astari
Loki Astari

Reputation: 264461

If an exception escapes main() it is implementation defined weather the stack is unwound.

try

int main()
{
    try
    {
        doWork(); // Do you experiment here. 
    }
    catch(...)
    {   /*
         * By catching here you force the stack to unwind correctly.
         */
        throw;  // re-throw so exceptions pass to the OS for debugging.
    }
}

Upvotes: 6

Edouard A.
Edouard A.

Reputation: 6128

You have an unhanded exception in the main, which means a call to terminate. Try this:

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


}

Upvotes: 6

Your A objects are not being destroyed because std::terminate is being called.

std::terminate is called when an unhandled exception leaks out of main. If you wrap your code in a try/catch (even if the catch just re-raises) you'll see the behaviour you were expecting.

Upvotes: 4

Stack Overflow is garbage
Stack Overflow is garbage

Reputation: 247999

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)

Upvotes: 18

David Thornley
David Thornley

Reputation: 57046

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.)

Upvotes: 5

Aaron Saarela
Aaron Saarela

Reputation: 4036

You don't have a handler for your exception. When this happens the standard says that std::terminate is called, which in turn calls abort. See section 14.7 in The C++ Programming Language, 3rd edition.

Upvotes: 20

Related Questions