是静态对象保证被初始化 [英] Is static object guaranteed to be initialized

查看:86
本文介绍了是静态对象保证被初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想了解静态对象的初始化。静态初始化看起来很简单,假设你理解常量表达式和 constexpr 。动态初始化似乎有点棘手。



[basic.start.init] / 4


它是实现定义是否静态存储持续时间的非局部变量的动态初始化是在第一个语句之前完成。如果初始化延迟到main的第一个语句之后的某个时间点,它应该发生在与要初始化的变量相同的翻译单元中定义的任何函数或变量的第一个odr使用之前。 >

footnote 34


具有带副作用的初始化的静态存储持续时间必须初始化,即使它不是odr使用的(3.2,3.7.1)。


[basic.start.init] / 5


它是实现定义的是非静态变量的动态初始化线程存储持续时间在线程的初始函数的第一个语句之前完成。如果初始化延迟到线程的初始函数的第一个语句之后的某个时间点,它将发生在任何变量的第一次使用(3.2)之前,线程存储持续时间在与变量相同的翻译单元中定义


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



h1.h

  #ifndef H1_H_ 
#define H1_H_

extern int count;

#endif

tu1.cpp

  #includeh1.h

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

S s;

tu2.cpp

  #includeh1.h

int main(int argc,char * argv [])
{
return count;
}

tu3.cpp

  #includeh1.h

int count;

因此,如果编译器推迟动态初始化,脚注34似乎表明 s 必须在某个点初始化。由于在转换单元中没有动态初始化的其他变量,所以没有其他变量来odr使用来强制初始化tu1中的变量。 s 保证已初始化?



主保证返回1?另外,有没有办法改变这个程序,使它不再保证返回1?






我有一个方法可以改变这个程序,打破代码,以便 s 的定义位于 main 中不同的翻译单元中。这避免了 main 是否使用odr的问题。假设 s 是翻译单元中唯一的对象,是否保证 main 将返回1?

解决方案

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

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


  1. 在开始 main 之前,可能会在其翻译单元中第一次使用任何内容之前初始化

  2. <

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


    当TU中的任何内容是ord使用时,则每个具有静态存储持续时间的非本地变量都必须初始化,即使是变量不是odr使用。


    因此,如果有一个TU,其中没有什么是ord使用,那么它的动态初始化可能不会发生。



    示例



    h1.h



      extern int count; 
    struct S
    {
    S();
    };



    h1.cpp



      #includeh1.h

    int count;
    S :: S()
    {
    ++ count;
    }



    h2.cpp



      #includeh1.h
    S s;



    main.cpp



      #includeh1.h
    #include< stdio.h>
    int main()
    {
    printf(%d \\\
    ,count);
    }

    这可以打印0或1:因为TU h2中的任何东西都不是odr-使用,它是未指定的代码初始化 s 将完成,如果有。



    编译器将在main之前初始化 s ,因此它将一定打印 1

      $ g ++ main.cpp h2.cpp h1.cpp -o test1 
    $ ./test1
    1

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

      $ g ++ -shared -fPIC h2.cpp -o h2.so 

    主文件现在是:



    main2.cpp



      #includeh1.h
    #include< dlfcn.h>
    #include< stdio.h>

    int main()
    {
    printf(%d\\\
    ,count);
    dlopen(./ h2.so,RTLD_NOW);
    printf(%d\\\
    ,count);
    return 0;
    }

    编译并运行:

      $ g ++ -shared -fPIC h2.cpp -o h2.so 
    $ g ++ -rdynamic main.cpp h1.cpp -ldl -o test2
    $ ./test2
    0
    1

    s 的初始化已延迟!好的部分是,不可能在动态加载库中引用任何东西,而无需首先加载它,并且加载它将触发动态初始化。所以一切都很好。



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


    I am trying to learn about initialization of static objects. Static initialization seems pretty straight forward, assuming you understand constant expressions and constexpr. Dynamic initialization seems quite a bit more tricky.

    [basic.start.init]/4

    It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first odr-use (3.2) of any function or variable defined in the same translation unit as the variable to be initialized.

    footnote 34

    A non-local variable with static storage duration having initialization with side-effects must be initialized even if it is not odr-used (3.2, 3.7.1).

    [basic.start.init]/5

    It is implementation-defined whether the dynamic initialization of a non-local variable with static or thread storage duration is done before the first statement of the initial function of the thread. If the initialization is deferred to some point in time after the first statement of the initial function of the thread, it shall occur before the first odr-use (3.2) of any variable with thread storage duration defined in the same translation unit as the variable to be initialized.

    I assume that "the initial function of the thread" refers to main, and not just threads started with std::thread.

    h1.h

    #ifndef H1_H_
    #define H1_H_
    
    extern int count;
    
    #endif
    

    tu1.cpp

    #include "h1.h"
    
    struct S
    {
       S()
       {
          ++count;
       }
    };
    
    S s;
    

    tu2.cpp

    #include "h1.h"
    
    int main(int argc, char *argv[])
    {
       return count;
    }
    

    tu3.cpp

    #include "h1.h"
    
    int count;
    

    So, if a compiler defers dynamic initialization, it seems that footnote 34 states that s must be initialized at some point. Since there are no other variables with dynamic initialization in the translation unit, there is no other variable to odr-use to force initialization of the variables in tu1. At what point is s guaranteed to have been initialized?

    Is main guaranteed to return 1? Also, is there some way to change this program such that it is no longer guaranteed to return 1? Alternatively, if it isn't guaranteed, is there some way to change this program such that it becomes guaranteed?


    I broke the code up so that the definition of s is in a different translation unit from main. This avoids the question of whether main is odr used. Given that s is the only object in the translation unit, is it guaranteed that main will return 1?

    解决方案

    I think that all this wording is there to describe what will happen in dynamic loaded libraries, but without explicitly naming them.

    To summarize how I interpret it: a non-local variable with static storage duration and dynamic initialization will:

    1. be initialized before the first odr-use of anything in its translation unit;
    2. possibly, before starting main, but possibly after it.

    I interpret the footnote 34 as (but remember that footnotes are not normative):

    When anything in a TU is ord-used, then every non-local variable with static storage duration having initialization with side-effects must be initialized, even the variables that are not odr-used.

    So, if there is a TU where nothing is ord-used, then its dynamic initializations may not happen.

    Example

    h1.h

    extern int count;
    struct S
    {
        S();
    };
    

    h1.cpp

    #include "h1.h"
    
    int count;
    S::S()
    {
       ++count;
    }
    

    h2.cpp

    #include "h1.h"
    S s;
    

    main.cpp

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

    This could print 0 or 1: since anything in TU h2 is never odr-used, it is unspecified when the code initialization of s will be done, if at all.

    Naturally, sane compilers will initialize s before main, so it will surely print 1:

    $ g++ main.cpp h2.cpp h1.cpp -o test1
    $ ./test1
    1
    

    Now, imagine that h2.cpp is in a shared library:

    $ g++ -shared -fPIC h2.cpp -o h2.so
    

    And the main file is now:

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

    Compile and run:

    $ g++ -shared -fPIC h2.cpp -o h2.so
    $ g++ -rdynamic main.cpp h1.cpp -ldl -o test2
    $ ./test2
    0
    1
    

    See? The initialization of s has been delayed! The nice part is that it is impossible to reference anything in the dynamic loaded library without first loading it, and loading it will trigger the dynamic initialization. So all is well.

    If you think that using dlopen is cheating, remember that there are compilers that support delay loading shared libraries (VC++ for example), where the system call to load the library will be generated automatically by the compiler just the first time it is needed.

    这篇关于是静态对象保证被初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆