构造变量的顺序与编译单元不同

时间:2013-09-17 15:26:34

标签: c++ g++

正如您所知,在函数外部定义的变量的构造顺序和不同的编译单元并不固定。有了这个理解,我设计了简单的代码,我认为必须在运行时崩溃。然而,它只是没有。这是代码。

/* table.h */
#include <iostream>
using namespace std;
class table {
public:
    int a;
    int b;
    table(table& t) {
        a = t.a;
        b = t.b;
    }
    void print(void) {
        cout << a << endl;
        cout << b << endl;
    }
};

/* f1.cpp */
#include "./table.h"
extern table t2;
table t1 = t2;

/* f2.cpp */
#include "./table.h"
extern table t1;
table t2 = t1;

/* core.cpp */
#include "./table.h"
extern table t1;
extern table t2;
int main(void) {
    t1.print();
    t2.print();
    return 0;
}

如您所见,t1和t2相互指代。虽然我们不确定哪一个会先被分配,但很明显,一个是访问另一个未分配的,这是一场彻底的灾难。这太奇怪了,它运作正常。

3 个答案:

答案 0 :(得分:2)

它不必崩溃 - 它只是未定义的行为。您永远不会使用实际值初始化成员变量,因此无论初始化的顺序如何,输出都将是垃圾。

由于它们是全局变量,因此在执行的最开始时保留/分配它们的内存空间。问题是构造函数的调用顺序。如果在构造对象之前引用该对象,则成员变量将具有已经发生的任何值。

答案 1 :(得分:1)

全局对象的初始化不是跨翻译单元排序的。

确保在使用之前初始化对象的典型方法是将其包装到如下函数中:

<强> myfoo.h:

#include "Foo.h"

Foo & myFoo();

<强> myfoo.cc:

#include "myfoo.h"

Foo & myFoo()
{
    static Foo impl;
    return impl;
}

每个需要全局对象的人都包括myfoo.h,并将全局对象称为myFoo()

如果你对你的问题尝试这种方法(适当修改以考虑初始化器),你会发现你的问题是不明确的,因为你将多次输入相同的静态初始化。

答案 2 :(得分:1)

  

我认为在运行时必须崩溃

没有;它具有(borderline)未定义的行为,但不要求未定义的行为导致崩溃。如果有,那么将定义行为。

  很明显,一个是访问另一个未分配的

它们已被分配,而不是(动态)初始化。静态对象的存储在运行任何用户代码之前的静态初始化阶段中进行分配和零初始化。然后,在动态初始化期间,使用另一个对象的零值存储器初始化每个对象。

  

这很奇怪它工作正常

正式地,在初始化之前访问对象的值会给出未定义的行为。实际上,您只需访问零值存储器而不指示错误。