(简单C ++概念)构造函数/析构函数调用的意外输出

时间:2011-12-06 05:41:51

标签: c++ inheritance constructor destructor

鉴于此代码:

#include <iostream>
using namespace std;

class Foo {
public:
    Foo ()          {   c = 'a'; cout << "Foo()" << endl;       }
    Foo (char ch)   {   c = ch; cout << "Foo(char)" << endl;    }
    ~Foo ()         {   cout << "~Foo()" << endl;               }

private:
    char c;
};

class Bar : public Foo {
public:
    Bar ()                      {   cout << "Bar()" << endl;    }
    Bar (char ch) : Foo(ch)     {   cout << "Bar(char)" << endl;    }
    ~Bar ()                     {   cout << "~Bar()" << endl;           }
};

Foo f1; static Bar b1;

int main()
{
    Bar b2;

    {
        static Foo f2('c');
        Foo f3;
        Bar b3 ('d');
    }

    return 0;
}

(您可以直接将其粘贴到编译器中)

我预期的样本输出的第一个部分是正确的:

Foo()
Foo() 
Bar() 
Foo()
Bar()
Foo(char) 
Foo()
Foo(char)
Bar(char)
~Bar()
~Foo
~Foo()
~Bar()
~Foo()
~Foo()

但是我得到了两个静态对象static Bar b1;static Foo f2('c');的析构函数输出错误。

最后一部分的正确答案是:

~Bar()
~Foo()
~Foo()

我明白了:

~Foo()
~Bar()
~Foo()

这是我的理由:

我知道在静态对象之前所有本地对象都被破坏了。在剩余的两个静态对象static Bar b1;static Foo f2('c');中,static Foo f2('c');出现 last ,因此它首先被破坏,因为析构函数被调用按照他们创造的相反顺序。

static Foo f2('c');首先没有被破坏,static Bar b1;是。为什么呢?

2 个答案:

答案 0 :(得分:3)

修改了你的程序:

#include <iostream>
using namespace std;

class Foo {
public:
    Foo ()          {   c = 'a'; cout << "Foo()" << endl;       }
    Foo (char ch)   {   c = ch; cout << "Foo(char)" << ch << endl;    }
    ~Foo ()         {   cout << "~Foo()"<< c << endl;               }

protected:
    char c;
};

class Bar : public Foo {
public:
    Bar ()                      {   cout << "Bar()" << endl;    }
    Bar (char ch) : Foo(ch)     {   cout << "Bar(char)" << ch << endl;    }
    ~Bar ()                     {   cout << "~Bar()" << c << endl;           }
};

Foo f1('a'); static Bar b1('b');

int main()
{
    Bar b2('c');

    {
        static Foo f2('d');
        Foo f3('e');
        Bar b3 ('f');
    }

    return 0;
}

在g ++ 4.5.2中生成以下输出:

Foo(char)a
Foo(char)b
Bar(char)b
Foo(char)c
Bar(char)c
Foo(char)d
Foo(char)e
Foo(char)f
Bar(char)f
~Bar()f
~Foo()f
~Foo()e
~Bar()c
~Foo()c
~Foo()d
~Bar()b
~Foo()b
~Foo()a

您会看到最后一个被破坏的是非静态全局变量Foo f1

编辑: 正如其他人所提到的,如果变量来自不同的翻译单元,那么具有静态存储持续时间的变量的初始化顺序是非特定的,但是当它们在同一个翻译单元中时可以定义它们。

构造函数调用的初始化(如本例所示)称为dynamic initialization

  

使用静态存储动态初始化非局部变量   持续时间是有序的还是无序的。明确的定义   专门的类模板静态数据成员已订购   初始化。其他类模板静态数据成员(即   隐式或显式实例化的特化)具有无序性   初始化。 具有静态存储持续时间的其他非局部变量   已订购初始化。有序初始化的变量   在单个翻译单元中定义的内容应在   它们在翻译单元中的定义顺序。

     

它是实现定义的动态初始化   具有静态存储持续时间的非局部变量在之前完成   主要的第一个声明。如果初始化推迟到某些   在主要的第一个陈述之后的时间点,它应该在之前发生   第一个odr-use(3.2)中定义的任何函数或变量   与要初始化的变量相同的翻译单元。

本地静态变量的初始化指定为

  

......这样的变量   在控制第一次通过其声明时初始化; ...

由于具有静态存储持续时间的变量的销毁应与其构造的顺序相反,因此在此示例中类型为FooBar的变量的构造和销毁顺序为事实上已定义。

同样,当你有多个翻译时,你最好不要依赖初始化的顺序。

答案 1 :(得分:-1)

请参阅this C++ FAQ entry,未定义静态对象的初始化顺序。 不要依赖它。