0

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

/* 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 相互引用。虽然我们不确定哪个会先分配,但很明显一个是访问另一个未分配的,这完全是一场灾难。这太奇怪了,它工作得很好。

4

3 回答 3

2

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

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

于 2013-09-17T15:30:28.973 回答
1

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

确保对象在使用前被初始化的典型方法是将其包装成这样的函数:

myfoo.h:

#include "Foo.h"

Foo & myFoo();

myfoo.cc:

#include "myfoo.h"

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

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

如果您在问题上尝试这种方法(适当修改以考虑初始化程序),您会发现您的问题定义不明确,因为您将多次输入相同的静态初始化。

于 2013-09-17T15:32:45.943 回答
1

我认为必须在运行时崩溃

不; 它具有(边界)未定义的行为,但未定义的行为不需要导致崩溃。如果有,那么行为将被定义。

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

它们已被分配,只是没有(动态)初始化。在运行任何用户代码之前的静态初始化阶段,分配静态对象的存储空间并进行零初始化。然后,在动态初始化期间,每个对象都使用另一个对象的零值内存进行初始化。

太奇怪了,它工作得很好

形式上,在初始化之前访问对象的值会给出未定义的行为。在实践中,您将简单地访问零值内存,而不指示错误。

于 2013-09-17T15:45:38.523 回答