Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/158.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何初始化非POD静态值?_C++_Static - Fatal编程技术网

C++ 如何初始化非POD静态值?

C++ 如何初始化非POD静态值?,c++,static,C++,Static,与其他一些语言不同,C++允许静态数据为任意类型,而不仅仅是普通的旧数据。普通的旧数据初始化起来很简单(编译器只是将值写入数据段中的适当地址),但其他更复杂的类型则不然 如何在C++中实现非POD类型的初始化?特别是,第一次执行函数foo时会发生什么情况?使用什么机制跟踪str是否已初始化 #include <string> void foo() { static std::string str("Hello, Stack Overflow!"); } #包括 void f

与其他一些语言不同,C++允许静态数据为任意类型,而不仅仅是普通的旧数据。普通的旧数据初始化起来很简单(编译器只是将值写入数据段中的适当地址),但其他更复杂的类型则不然

如何在C++中实现非POD类型的初始化?特别是,第一次执行函数

foo
时会发生什么情况?使用什么机制跟踪
str
是否已初始化

#include <string>
void foo() {
    static std::string str("Hello, Stack Overflow!");
}
#包括
void foo(){
静态std::string str(“你好,堆栈溢出!”);
}

它是特定于实现的

通常,会有一个标志(静态初始化为零)来指示它是否已初始化,以及(在C++11或早期的线程安全实现中)某种类型的互斥,也可以静态初始化,以防止多个线程试图初始化它

生成的代码通常会按照

static __atomic_flag_type __initialised = false;
static __mutex_type __mutex = __MUTEX_INITIALISER;

if (!__initialised) {
    __lock_type __lock(__mutex);
    if (!__initialised) {
        __initialise(str);
        __initialised = true;
    }
}

C++11要求函数local
static
变量的初始化是线程安全的。因此,至少在兼容的编译器中,通常会使用某种类型的同步原语,每次输入函数时都需要对其进行检查

例如,以下是此程序代码的示例:

#include <string>
void foo() {
    static std::string str("Hello, Stack Overflow!");
}

int main() {}
#包括
void foo(){
静态std::string str(“你好,堆栈溢出!”);
}
int main(){}

.LC0:
.string“你好,堆栈溢出!”
foo():
cmpb$0,foo()的保护变量::str(%rip)
乙脑L14
ret
.L14:
pushq%rbx
foo()的movl保护变量:str,%edi
subq$16,%rsp
呼叫(cxa)警卫(guard)(acquire)
testl%eax,%eax
约15
.L1:
加成16美元,rsp%
popq%rbx
ret
.L15:
leaq 15(%rsp),%rdx
movl$.LC0,%esi
movl foo():str,%edi
调用std::basic_string::basic_string(char const*,std::allocator const&)
foo()的movl保护变量:str,%edi
呼叫(cxa)(cxa)(guard)(释放)
movl$\uU dso\u句柄%edx
movl foo():str,%esi
movl std::basic_string::~basic_string(),%edi
调用u_cxa_atexit
jmp.L1
movq%rax,%rbx
foo()的movl保护变量:str,%edi
呼叫\uuuucxa\uguard\uABORT
movq%rbx,%rdi
打电话(解除)(恢复)
主要内容:
xorl%eax,%eax
ret

\uuuucxa\u guard\u acquire
\uucxa\u guard\u release
等都在保护静态变量的初始化。

我看到的实现使用了一个隐藏的布尔变量来检查变量是否已初始化。现代编译器将安全地执行此线程,但IIRC,一些较旧的编译器没有这样做,如果同时从多个线程调用它,则可以调用两次构造函数

大致如下:

static bool __str_initialized = false;
static char __mem_for_str[...]; //std::string str("Hello, Stack Overflow!");

void foo() {
    if (!__str_initialized)
    {
        lock();
        __str_initialized = true;
        new (__mem_for_str) std::string("Hello, Stack Overflow!");
        unlock();
    }
}
然后,在程序的结束代码中:

if (__str_initialized)
     ((std::string&)__mem_for_str).~std::string();

您可以通过生成汇编程序列表来检查编译器的功能

调试模式下的MSVC2008生成此代码(不包括异常处理prolog/epilog等):


i、 e有一个静态变量被
?$S1@?1??foo@@YA引用_NXZ@4IA
这将被检查以查看&1是否为零。如果不是,则分支到标签
$LN1@foo:
。否则,它或位于标志的1中,在已知位置构造字符串,然后使用“atexit”在程序出口添加对其析构函数的调用。然后继续正常运行。

实施细节。一种可能的变体是在初始化之后将函数指针移动到。@sp2danny我怀疑这是一个实现细节。这就是为什么我问它是如何“典型地”实现的关于移动函数指针:我考虑过这一点,但在我看来更可能在函数的开头插入一条跳转指令,这会跳过初始化。通过这种方式,指向该函数的指针仍然有效,即使该函数在调用之前或没有调用。有趣的是,我们看到了关于使用其他static进行静态初始化的解释:)@Jarod42:的确如此。这就是理解C++中“静态”的各种重载意义的重要因素;特别是静态存储持续时间和静态初始化之间的差异。
if (__str_initialized)
     ((std::string&)__mem_for_str).~std::string();
    mov eax, DWORD PTR ?$S1@?1??foo@@YA_NXZ@4IA
    and eax, 1
    jne SHORT $LN1@foo
    mov eax, DWORD PTR ?$S1@?1??foo@@YA_NXZ@4IA
    or  eax, 1
    mov DWORD PTR ?$S1@?1??foo@@YA_NXZ@4IA, eax
    mov DWORD PTR __$EHRec$[ebp+8], 0
    mov esi, esp
    push    OFFSET ??_C@_0BH@ENJCLPMJ@Hello?0?5Stack?5Overflow?$CB?$AA@
    mov ecx, OFFSET ?str@?1??foo@@YA_NXZ@4V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@A
    call    DWORD PTR __imp_??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z
    cmp esi, esp
    call    __RTC_CheckEsp
    push    OFFSET ??__Fstr@?1??foo@@YA_NXZ@YAXXZ   ; `foo'::`2'::`dynamic atexit destructor for 'str''
    call    _atexit
    add esp, 4
    mov DWORD PTR __$EHRec$[ebp+8], -1
$LN1@foo: