I came across this in chapter 10, Thinking in C++ vol.1 by Bruce Eckel.
Destructors for static objects (that is, all objects with static storage, not just local static objects as in the example above) are called when main( ) exits or when the Standard C library function exit( ) is explicitly called. In most implementations, main( ) just calls exit( ) when it terminates. This means that it can be dangerous to call exit( ) inside a destructor because you can end up with infinite recursion
I also came across this question in SO. Then, I wrote a code myself to simulate this recursion but I failed. Here's how my code looks like.
#include <iostream>
#include <cstdlib>
class Test
{
private:
std::string _name;
static Test obj1;
static Test obj2;
public:
Test(std::string name)
{
std::cout << "in constructor:" << name << std::endl;
_name = name;
}
~Test()
{
std::cout << "in destructor:" << _name << std::endl;
if ("class static 2" == _name)
{
std::cout << "calling exit" << std::endl;
exit(2);
std::cout << "should not print this" << std::endl;
}
}
};
Test global("global");
Test Test::obj1("class static 1");
Test Test::obj2("class static 2");
int main(void)
{
static Test mainStatic_1("main static");
return 0;
}
And this is the output I got, without any infinite looping
in constructor:global
in constructor:class static 1
in constructor:class static 2
in constructor:main static
in destructor:main static
in destructor:class static 2
calling exit
in destructor:class static 1
in destructor:global
Is my compiler smart enough to handle this? I am using GCC 4.7.2 on Ubuntu.
I'm pretty sure this comes under the heading of "undefined behaviour". I can't find a paragraph in the C++ standard to say so, but it's pretty obvious that if you call
exit()
from something that happened because you calledexit()
, chances are that you end up in an infinite loop. It is not guaranteed, because the handling of the global destructors may be done in such a way that the list of things to destroy is dealt with by first removing it from some the list [1], then calling the destructor. So ifexit
is called again, it will be in a state where it "has already dealt with this object".The standard certainly doesn't say "You must cope with
exit()
being called multiple times - so it's quite possible that another C++ library will fail to cope with this.And being pedantic: It's not so much the compiler as the C++ or classic C runtime library that deals with this.
[1] By list, I'm not saying
std::list
, but a generic "Some sort of container holding what needs to be destroyed".It would appear that they have added some safeguards for it, but it is still a bad practice nonetheless.