C++:何时(以及如何)调用C++全局静态构造函数?

Mat*_*lin 24 c++ static gcc constructor global

我正在研究一些C++代码,我遇到了一个问题,这个问题一直在唠叨我...假设我正在Linux主机上用GCC编译ELF目标,那里是全局静态构造函数和析构函数叫什么名字?

我听说crtbegin.o中有一个函数_init,crtend.o中有函数_fini.这些是由crt0.o调用的吗?或者动态链接器是否实际检测到它们在加载的二进制文件中的存在并调用它们 如果是这样,它实际上给他们打电话?

我主要感兴趣的是,因为我的代码被加载,执行,然后在运行时卸载,所以我可以理解幕后发生的事情.

提前致谢!

更新:我基本上试图找出构造函数被调用的一般时间.我不想根据这些信息在我的代码中做出假设,或多或少地更好地了解我的程序加载时在较低级别发生的事情.我知道这是特定于操作系统的,但我试图在这个问题上稍微缩小一点.

Lea*_*elo 18

在谈论非本地静态对象时,没有太多保证.正如您已经知道的(这里也提到过),它不应该编写依赖于它的代码.静态初始化命令惨败...

静态对象经历两阶段初始化:静态初始化和动态初始化.前者首先发生,并通过常量表达式执行零初始化或初始化.后者在所有静态初始化完成后发生.例如,这是在调用构造函数时.

通常,此初始化发生在main()之前的某个时间.然而,与许多人认为的相反,即使C++标准无法保证.实际上保证的是,在使用与正被初始化的对象相同的转换单元中定义的任何函数或对象之前完成初始化.请注意,这不是特定于操作系统的.这是C++规则.以下是标准的引用:

它是实现定义的,无论命名空间作用域的对象的动态初始化(8.5,9.4,12.1,12.6.1)是否在main的第一个语句之前完成.如果初始化被推迟到main的第一个语句之后的某个时间点,则它应该在第一次使用与要初始化的对象相同的转换单元中定义的任何函数或对象之前发生.

  • 这不是惨败.这只是你需要注意的事情.停止引用这样的C++ Lite FAQ. (3认同)

Gun*_*iez 11

这不是特定于操作系统,而是特定于编译器.

你给出了答案,初始化完成了__init.

对于第二部分,在gcc中,您可以保证初始化的顺序,____attribute____((init_priority(PRIORITY)))附加到变量定义,其中PRIORITY是一些相对值,首先初始化较小的数字.


ebo*_*ebo 10

这很大程度上取决于编译器和运行时.对全局对象构造的时间做出任何假设并不是一个好主意.

如果你有一个静态对象依赖于已经构造的另一个静态对象,这尤其成问题.

这称为" 静态初始化顺序惨败 ".即使代码中不是这种情况,关于该主题的C++ Lite FAQ文章也值得一读.

  • 只有C ++ Lite FAQ称它为惨败。当您知道它确实存在时,它并不是什么大问题。C ++ Lite FAQ充斥着“ Marshall”个人观点,但并非所有观点都很好。 (2认同)

Mar*_*ork 6

您拥有的受助人:

  • 全局命名空间中的所有静态非本地对象都是在main()之前构造的
  • 在使用该命名空间中的任何函数/方法之前构造另一个命名空间中的所有静态非本地对象(因此允许编译器可能懒惰地评估它们[但不依赖于此行为]).
  • 翻译单元中的所有静态非本地对象都按声明的顺序构造.
  • 关于翻译单元之间的顺序没有任何定义.
  • 所有静态非本地对象都以与创建相反的顺序销毁.(这包括静态函数变量(在首次使用时延迟创建).

如果你有彼此依赖的全局变量,你有两个选择:

  • 将它们放在同一个翻译单元中.
  • 将它们转换为在首次使用时检索和构造的静态函数变量.

示例1:Global A的构造函数使用全局日志

class AType
{    AType()  { log.report("A Constructed");}};

LogType    log;
AType      A;

// Or 
Class AType() 
{    AType()  { getLog().report("A Constructed");}};
LogType& getLog()
{
    static LogType  log;
    return log;
}
// Define A anywhere;
Run Code Online (Sandbox Code Playgroud)

示例Global B的析构函数使用全局日志

在这里,你必须承认对象日志在对象B之前没有被销毁.这意味着必须在B之前完全构造日志(因为随后将应用销毁规则的相反顺序).同样可以使用相同的技术.要么将它们放在同一个翻译单元中,要么使用函数来获取日志.

class BType
{    ~BType()  { log.report("B Destroyed");}};

LogType    log;
BType      B;   // B constructed after log (so B will be destroyed first)

// Or 
Class BType() 
{    BType()    { getLog();}
     /*
      * If log is used in the destructor then it must not be destroyed before B
      * This means it must be constructed before B 
      * (reverse order destruction guarantees that it will then be destroyed after B)
      *
      * To achieve this just call the getLog() function in the constructor.
      * This means that 'log' will be fully constructed before this object.
      * This means it will be destroyed after and thus safe to use in the destructor.
      */
    ~BType()    { getLog().report("B Destroyed");}
};
LogType& getLog()
{
    static LogType  log;
    return log;
}
// Define B anywhere;
Run Code Online (Sandbox Code Playgroud)

  • hack:=>通用代码依赖于特定于硬件/ OS /编译器的功能. (3认同)

sbi*_*sbi 5

根据C++标准,在使用其翻译单元的任何功能或对象之前调用它们.请注意,对于全局命名空间中的对象,这意味着它们在main()调用之前已初始化.(参见ltcmeloMartin关于mote细节答案以及对此的讨论.)