【问题标题】:What exactly does "static initialization" mean?“静态初始化”到底是什么意思?
【发布时间】:2017-06-29 21:13:15
【问题描述】:

我一直在阅读 C++11 中的 POD,并且我读过的几个地方都谈到了支持静态初始化的 POD。例如:

On StackOverflow:

POD 的想法基本上是捕获两个不同的属性:
1. 支持静态初始化
2. 用 C++ 编译 POD 可以得到与用 C 编译的结构相同的内存布局。

(只有粗体部分是相关的)

On Wikipedia:

可以静态初始化平凡的类型。

显然我不明白什么是静态初始化。我认为创建全局变量是静态初始化的一个示例,但我可以执行以下操作,但 Foo 不是 POD:

#include <type_traits>
#include <iostream>

struct Foo {
  Foo() : x(0), y(0) {}
  int x;
  int y;
};

struct Bar {
  Bar() = default;
  int x;
  int y;
};

// Apparently the following two lines are not "static initialization" because
// Foo is non-POD yet we can still do this:
Foo f;
Bar b;

int main()
{
    if (std::is_pod<Foo>::value) std::cout << "Foo is a POD" << std::endl;
    else                         std::cout << "Foo is *not* a POD" << std::endl;

    if (std::is_pod<Bar>::value) std::cout << "Bar is a POD" << std::endl;
    else                         std::cout << "Bar is *not* a POD" << std::endl;
}

Output:

Foo is *not* a POD
Bar is a POD

那么静态初始化到底是什么,它与普通类有什么关系呢?例子会很棒。

【问题讨论】:

  • 据我所知,POD 不能有构造函数或析构函数。 Bar 并没有真正定义构造函数,因为它使用的是默认值。
  • @crush nontrivial
  • @BartekBanachewicz 你是什么意思?也许我也需要这节课!
  • @crush: POD 不能有用户定义的构造函数(所以是的,Foo 不是 POD,但 Bar 是),但现在我对什么是静态的感到困惑初始化是因为我可以使全局Foos 尽管它不是 POD,所以显然我对静态初始化的理解是错误的。
  • @Cornstalks:你可以认为它已经在你的可执行文件包含的字节中初始化,而不是为了初始化它需要运行的代码。

标签: c++ c++11


【解决方案1】:

静态持续时间的对象的初始化分为两遍,静态初始化和动态初始化(注意术语的滥用>静态 :))。

动态初始化是指涉及调用函数的初始化,因此必须在运行时进行,而文字初始化则可以存储在可执行文件本身中并刚刚加载。

【讨论】:

  • 真棒答案+1,在这个世界上,只有少数人有幸拥有教学和使事情变得非常好理解的天赋,坦率地说,你是那些宝石之一,我真的觉得你的答案非常清晰: )
【解决方案2】:

静态初始化适用于具有静态或线程存储持续时间的变量。它分两个阶段发生。

首先,具有静态存储持续时间的变量在任何其他初始化之前被初始化为零。

然后执行常量初始化。常量初始化必须是以下三种可能性之一(第 3.6.2/2 节):

  • 如果出现在具有静态或线程存储持续时间的引用的初始化程序中的每个完整表达式(包括隐式转换)都是一个常量表达式(5.19)并且该引用绑定到一个左值,该左值指定具有静态存储持续时间的对象或到一个临时的(见 12.2);

  • 如果具有静态或线程存储持续时间的对象由构造函数调用初始化,如果构造函数是 constexpr 构造函数,如果所有构造函数参数都是常量表达式(包括转换),并且如果在函数调用替换之后(7.1.5 ),在 mem-initializers 和用于非静态数据成员的大括号或等号初始化器中的每个构造函数调用和完整表达式都是一个常量表达式;

  • 如果具有静态或线程存储持续时间的对象未由构造函数调用初始化,并且出现在其初始化程序中的每个完整表达式都是常量表达式。

任何其他初始化(甚至是全局变量)都是动态初始化。但是,允许编译器将动态初始化视为静态初始化,前提是这样做不会改变程序的外部可见效果,假设它可以计算出正确的值(即使该值没有正式限定为常量表达式)。

constant expression 的定义(不幸的是)相当长且复杂。定义的基本风格是将core constant expression定义为任何表达式,除了那些符合例外列表的表达式(不幸的是,它超过一页,因此将其总结为快速且易于理解的内容并不容易)。

【讨论】:

    【解决方案3】:

    静态初始化是使用编译时值初始化一些变量,以便该值最终被“烘焙”到可执行映像中(实际上不需要运行代码):

    struct Foo {
      int x;
      int y;
    };
    
    Foo foo = { 0, 1 };
    

    在上面的例子中struct Foo是POD,所以编译器知道它的内存布局只是两个相邻的整数。它也知道foo.x应该初始化为0foo.y初始化为1。这些信息足以生成关于foo在编译时的外观的“内存映像”,并将其写入可执行映像。

    稍后运行映像时,操作系统加载程序将其内容映射到内存地址,从而使foo“活着”。重要的是,foo 的“初始化”实际上已经在进程(包括您的代码以及 C/C++ 运行时中的代码,它们首先运行)有时间执行甚至单个 CPU 指令之前完成.

    【讨论】:

    • 那么,因为没有构造函数尝试初始化变量,所以不需要运行代码?在本例中,x 和 y 是否未初始化?
    • @crush:一个重要的构造函数肯定会使类型非静态初始化。在此示例中,xy 分别初始化为 0 和 1。我又加了几句,现在好点了吗?
    • 是否允许用户执行Foo foo; 而不提供初始化程序?感谢您的解释!
    • @crush:是的。但是成员的初始化现在取决于上下文。如果您将Foo foo; 声明为全局(即静态存储持续时间),它将被初始化为使两个成员都为零。这将是“静态初始化”。
    • 我认为下面的答案回答了我的问题:First, variables with static storage duration are zero-initialized before any other initialization.
    【解决方案4】:

    静态成员初始化发生在类范围内。因此,他们可以访问其他成员数据或函数。

    引用自MSDN

    这里有一个很好的例子来说明它是如何工作的。

    【讨论】:

    • 静态存储持续时间与静态类成员正交。
    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
    • @iehrlich 不过,它没有回答这个问题。发帖人实际上并没有阅读问题,只是挑选了几个单词,然后发布了一个关于完全不同但巧合地使用相同单词的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    • 2012-02-10
    • 1970-01-01
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多