【问题标题】:(Simple C++ Concepts) Unexpected output of constructor/destructor calls(简单 C++ 概念)构造函数/析构函数调用的意外输出
【发布时间】:2012-01-13 19:33:53
【问题描述】:

鉴于此代码:

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

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

我预期的示例输出的 first 部分是正确的:

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,所以它被first 销毁,因为析构函数的调用顺序与它们的相反。创作。

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

【问题讨论】:

  • 你应该添加一个成员变量来区分不同的对象。
  • 仅供参考:我得到了你在使用 g++ 4.6.1 时所期望得到的结果
  • C++ 与 Eclipse (MinGW) 和 Microsoft Visual Studio(禁用语言扩展)仍然输出“正确”的答案,而不是我的预期输出。

标签: c++ inheritance constructor destructor


【解决方案1】:

修改了你的程序:

#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,并且

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

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

局部静态变量的初始化指定为

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

并且由于静态存储时长的变量的销毁应该与它们的构造顺序相反,所以本例中FooBar类型的变量的构造和销毁顺序实际上是定义好的。

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

【讨论】:

  • 我明白了...'a' 是一个全局对象,而不是本地对象。全局和静态对象按照它们的创建顺序被销毁。它们的创建顺序是 a > b > b > d,因此它们的破坏顺序必须是 d > b > b > a。我的错误是将“a”视为本地对象。它是一个全局对象,因为它是在 main() 之外声明的。
【解决方案2】:

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

【讨论】:

  • -1“静态对象的初始化顺序未定义”不正确,这不是常见问题解答所说的。不同翻译单元之间的初始化顺序是未指定的。翻译单元内的顺序通常是明确定义的。 C++11 §3.6.2/2 “明确专门化的类模板静态数据成员的定义已有序初始化。其他类模板静态数据成员(即,隐式或显式实例化的专门化)具有无序初始化。其他具有静态的非局部变量存储持续时间已有序初始化。”
猜你喜欢
  • 2014-10-07
  • 2015-09-19
  • 1970-01-01
  • 1970-01-01
  • 2019-05-14
  • 2021-07-19
  • 2013-04-17
  • 2011-04-16
  • 2013-01-01
相关资源
最近更新 更多