是否保证静态对象被初始化

Gra*_*rak 13 c++ c++11

我试图了解静态对象的初始化.假设你理解常量表达式,静态初始化似乎很简单constexpr.动态初始化似乎有点棘手.

[basic.start.init/4

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

脚注34

具有静态存储持续时间的非局部变量必须初始化,即使它没有使用odr-used(3.2,3.7.1).

[basic.start.init]/5

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

我假设"线程的初始函数"是指main,而不仅仅是以std :: thread开头的线程.

h1.h

#ifndef H1_H_
#define H1_H_

extern int count;

#endif
Run Code Online (Sandbox Code Playgroud)

tu1.cpp

#include "h1.h"

struct S
{
   S()
   {
      ++count;
   }
};

S s;
Run Code Online (Sandbox Code Playgroud)

tu2.cpp

#include "h1.h"

int main(int argc, char *argv[])
{
   return count;
}
Run Code Online (Sandbox Code Playgroud)

tu3.cpp

#include "h1.h"

int count;
Run Code Online (Sandbox Code Playgroud)

因此,如果编译器推迟动态初始化,脚注34似乎表明s必须在某个时刻进行初始化.由于在转换单元中没有其他具有动态初始化的变量,因此没有其他变量可用于强制初始化tu1中的变量.什么时候s保证已经初始化?

主要保证返回1吗?还有,有什么方法可以改变这个程序,使其不再保证返回1?或者,如果不能保证,有没有办法改变这个程序,以保证它?


我打破了代码,以便定义与s不同的翻译单元main.这避免了是否main使用odr 的问题.鉴于这s是翻译单元中唯一的对象,是否保证main会返回1?

rod*_*igo 5

我认为所有这些措辞都是为了描述动态加载库中会发生什么,但没有明确命名它们.

总结我如何解释它:具有静态存储持续时间和动态初始化的非局部变量将:

  1. 在翻译单元中的任何事物的第一次使用之前进行初始化;
  2. 可能,在开始之前main,但可能在之后.

我将脚注34解释为(但请记住,脚注不是规范性的):

当TU中的任何东西被ord-used使用时,每个具有静态存储持续时间的非局部变量都必须初始化,即使是没有使用过的变量也是如此.

因此,如果存在没有使用任何内容的TU,则其动态初始化可能不会发生.

h1.h

extern int count;
struct S
{
    S();
};
Run Code Online (Sandbox Code Playgroud)

h1.cpp

#include "h1.h"

int count;
S::S()
{
   ++count;
}
Run Code Online (Sandbox Code Playgroud)

h2.cpp

#include "h1.h"
S s;
Run Code Online (Sandbox Code Playgroud)

main.cpp中

#include "h1.h"
#include <stdio.h>
int main()
{
    printf("%d\n", count);
}
Run Code Online (Sandbox Code Playgroud)

这可以打印0或1:因为TU h2中的任何内容都不会被使用,所以当完成代码初始化时,它是未指定的s,如果有的话.

当然,理智的编译器会s在main之前初始化,因此它肯定会打印出来1:

$ g++ main.cpp h2.cpp h1.cpp -o test1
$ ./test1
1
Run Code Online (Sandbox Code Playgroud)

现在,假设它h2.cpp位于共享库中:

$ g++ -shared -fPIC h2.cpp -o h2.so
Run Code Online (Sandbox Code Playgroud)

主文件现在是:

main2.cpp

#include "h1.h"
#include <dlfcn.h>
#include <stdio.h>

int main()
{
    printf("%d\n", count);
    dlopen("./h2.so", RTLD_NOW);
    printf("%d\n", count);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译并运行:

$ g++ -shared -fPIC h2.cpp -o h2.so
$ g++ -rdynamic main.cpp h1.cpp -ldl -o test2
$ ./test2
0
1
Run Code Online (Sandbox Code Playgroud)

看到?初始化s已被推迟!好的部分是,如果没有先加载它就不可能在动态加载库中引用任何内容,加载它将触发动态初始化.一切都很好.

如果您认为使用dlopen是作弊,请记住有编译器支持延迟加载共享库(例如VC++),其中加载库的系统调用将在第一次需要时由编译器自动生成.

  • @DyP:[basic.stc.static]/2禁止编译器消除初始化变量,即使它未被使用.但是[basic.start.init]/4允许无限期地延迟其初始化,如果TU没有使用的话(我认为`elimination`是一个优化术语,顺便说一句).因此,如果TU我们没有使用odr,变量必须在那里,但它可能在整个程序运行期间未初始化.并且应用原则规则,就好像变量根本不存在! (2认同)