我正在编写一个快速示例来演示全局对象的初始化/销毁。这样做,我遇到了以下难题。
一般来说,您不应在头文件中包含头文件中代码不需要的任何标头。这有助于减少混淆和编译时间。
在我的例子中,我从类定义中分离出构造函数和析构函数的定义。在我的情况下,它们是如此微不足道,我可能只是内联它们,但这只是一个例子。
实际定义构造函数和析构函数的转换单元包含iostream
,以便它可以将调用输出到控制台。
我的问题出现在我们开始讨论在全球范围内声明我的班级实例的时候。现在我遇到了初始化顺序和翻译单元。转换单元内全局变量的初始化顺序已明确定义。全局变量从一个翻译单元到另一个翻译单元的初始化顺序没有明确规定。在我的示例中,它可以执行以下操作之一:
24.4.1 / 2:
构造对象 [在我的情况下为std :: cout] ,并且在构建类ios_base::Init
的对象的第一次之前或期间的某个时间建立关联,并且在任何时间建立关联在主体开始执行之前的情况。程序执行期间不会销毁对象。在翻译单元中包含<iostream>
的结果应该好像<iostream>
定义了具有静态存储持续时间的ios_base::Init
实例。同样,整个程序的行为应该至少有一个具有静态存储持续时间的ios_base::Init
实例。
请注意,此段落调用as if规则,因此它不必创建ios_base::Init
的实例,但它必须表现得像它一样。
假设编译器和标准库的行为与段落中的指定相同,并且不做不同但等效的操作,似乎对我的程序有效的唯一初始化顺序是初始化第一个源文件,然后是第二个源文件。否则std :: cout在尝试使用之前不会被初始化。
标题文件:
#ifndef HEADER_H_
#define HEADER_H_
struct A {
A(const char*);
~A();
const char* v;
}
#endif
第一个源文件:
#include "header.h"
#include <iostream>
A::A(const char* val) : v{ val } {
std::cout << v << "\n";
}
~A() {
std::cout << "~" << v << "\n";
}
A a{ "a" };
A b{ "b" };
第二个源文件:
#include "header.h"
A c{ "c" };
A d{ "d" };
int main() {
}
答案 0 :(得分:0)
在写这个问题时,我意识到答案包含在3.6.2 / 4中。
实现 - 定义具有静态存储持续时间的非局部变量的动态初始化是否在main的第一个语句之前完成。如果初始化被推迟到第一个main语句之后的某个时间点,它应该在第一次使用与要初始化的变量相同的转换单元中的任何函数或变量之前发生。
这意味着即使它决定只初始化第二个源文件,对c
构造函数的调用也会导致第一个源文件在实际执行{{1}的构造函数之前被初始化}。这保证了静态初始化完成的顺序是:
c
ios_base::Init
a
b
c
由于初始化顺序没有歧义,所以没有问题。