tags:

views:

115

answers:

5
+2  Q: 

Exceptions - c++

Hi,

I'm trying to understand the behavior of exceptions in c++. I wrote the following code:

class A{
public: 
    A(){
    };
    ~A(){
        cout<<"hello";

    };
};
int exceptionTest(){

    throw "blablabla";
};

int main(){

        A sd;
    int test = exceptionTest();
     return 0;
}

I've noticed that in this case the distructor gets called even though no one caught the exception. If I change the "main" code to:

 int main(){

            A* sd = new A();
        int test = exceptionTest();
         return 0;
    }

The distructor will not be called. Can anyone please tell me what is the reason for the different behavior?

Thanks, Li

+8  A: 

The fact that you are throwing an exception is irrelevant here. In your first example, sd is an object that exists on the stack. When execution exits its scope, for whatever reason, it gets destroyed. In the second example, sd is a pointer to an object that was explicitly allocated using new. This object will not be destroyed until that pointer is passed to delete; since you never do so, your program is currently leaking it.

moonshadow
+2  A: 

Local variable destructors are called automatically, as soon as the variable is out of scope. Destructors are never called on pointers, so you must call it yourself.

Draco Ater
+2  A: 

I've noticed that in this case the distructor gets called even though no one caught the exception.

That's exactly what to expect.

This mechanism is a RAII consequence that makes you "sure" that resources will be freed even if there is an exception. For example :

class File
{
   public:
   File( const std::string filename ) : file_handler(file_open( filename )) { } // whatever the implementation

   ~File() { file_close(file_handler); }

   private:
   FileHandler file_handler;
};

void test(){ throw "This is a test"; }


int main()
{
   File file("test.txt");
   test();
   return false;
}

You're assured that the file will be closed even with the throw. So if you use RAII to manage your resources.

That's because when the exception is thrown, until it get catch, it goes back in the call stack and if there is no catch the local objects are destroyed the way they would be if we got out of scope.

Klaim
The destructor being called is *not* what I would expect (and it doesn't with codepad.org). If an unhandled exception is thrown, no stack unwinding is required and the program may call `terminate()` right away. Probably the language allows for stack unwinding in this case, though, and asker's compiler does it anyway. - Only if there is a suitable catch clause for the exception, can you rely on destructors being called, for uncaught exception this may (as it appears) or may not happen.
visitor
That would require a verification from the sandard document but I don't have it. If you have it you could tell me where you've read that? Thanks in advance.
Klaim
Posted my own answer.
visitor
+4  A: 

The standard has the following to say on the matter:

-9- If no matching handler is found in a program, the function terminate() is called; whether or not the stack is unwound before this call to terminate() is implementation-defined.

So your compiler performs stack unwinding (invoking destructors of locals), others may not. For example, with G++ or codepad.org, this program will not output "hello".


Dynamically allocated objects are not destroyed until you explicitly destroy them (with delete or such). In particular, if an exception occurs in the meantime, code may never reach the deallocation statement.

visitor
A: 

This is not really an answer, but I might clarify the behavior, in case of RAII mechanism, that I understood from the other answer and Mike's comments.

#include <iostream>

class Bar
{
        public:
        Bar() { std::cout << "Bar constructor" << std::endl; }
        ~Bar() { std::cout << "Bar destructor" << std::endl; }
};

void foo()
{
        throw("Exception");
}

int main()
{
        // Variation, add { to create a new scope
        Bar bar;
        foo();
        // Variation : }
        return 0;
}

Using g++, this code, where the exception is not catched will output the following:

Bar constructor
terminate called after throwing an instance of 'char const*'
Aborted

Meaning that g++ does not unwind the stack (or let go the variable out of scope, if I understand the "variant" correctly), so the destructor is not called.

However, if you catch the exception:

#include <iostream>

class Bar
{
        public:
        Bar() { std::cout << "Bar constructor" << std::endl; }
        ~Bar() { std::cout << "Bar destructor" << std::endl; }
};

void foo()
{
        throw("Exception");
}

int main()
{
        try
        {
                Bar bar;
                foo();
        }
        catch (...)
        {
                // Nothing here
        }
        return 0;
}

then the output will be

Bar constructor
Bar destructor

and you recover the correct behavior.

Cedric H.