0

我在 Bruce Eckel 的第 10 章 Thinking in C++ vol.1 中遇到了这个问题。

当 main() 退出或显式调用标准 C 库函数 exit() 时,将调用静态对象(即所有具有静态存储的对象,而不仅仅是如上例中的本地静态对象)的析构函数。在大多数实现中,main() 只是在终止时调用 exit()。这意味着在析构函数中调用 exit() 可能很危险,因为最终可能会出现无限递归

我也在SO 中遇到过这个问题。然后,我自己编写了一个代码来模拟这种递归,但我失败了。这是我的代码的样子。

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

这是我得到的输出,没有任何无限循环

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

我的编译器足够聪明来处理这个问题吗?我在 Ubuntu 上使用 GCC 4.7.2。

4

2 回答 2

2

我很确定这属于“未定义行为”的标题。我在 C++ 标准中找不到这样说的段落,但很明显,如果你exit()从发生的事情中调用,因为你调用了exit(),你很可能最终陷入无限循环。不能保证,因为全局析构函数的处理可能会以这样一种方式完成,即首先将要销毁的事物列表从某些列表 [1] 中删除,然后调用析构函数。所以如果exit再次调用,它将处于“已经处理过这个对象”的状态。

标准当然没有说“你必须应对exit()被多次调用 - 所以另一个 C++ 库很可能无法应对这个问题。

并且是迂腐的:与其说是编译器,不如说是处理这个问题的 C++ 或经典的 C 运行时库。

[1] 按列表,我不是在说std::list,而是一个通用的“某种容器,里面装着需要销毁的东西”。

于 2013-07-13T09:31:54.203 回答
1

我的编译器足够聪明来处理这个问题吗?

看起来他们已经为此添加了一些保护措施,但这仍然是一种不好的做法。

于 2013-07-13T07:45:54.220 回答