嗨我有静态std :: map和一些值,静态迭代器是这样的默认元素,并立即初始化:
<。>文件中的
class foo
{
static std::map<std::string, int> sqlenumToInt;
static std::map<std::string, int> initEnumToInt();
static std::map<std::string, int>::iterator defaultIt;
};
在.c文件中
std::map<std::string, int> foo::sqlenumToInt = initEnumToInt();
std::map<std::string, int> foo::defaultIt = std::map<std::string, int>::iterator();
std::map<std::string, int> foo::initEnumToInt();
{
std::map<std::string, int> test;
defaultIt = test.insert(std::make_pair("a", 0)).first
test["b"] = 2;
test["c"] = 3;
test["d"] = 4;
return test;
}
静态变量初始化的默认顺序是什么。将是默认的 的std ::地图:迭代() 或iterator到sqlenumToInt的第一个元素
答案 0 :(得分:3)
在翻译单元中,很好地定义了静态变量的初始化顺序;静态变量按定义的顺序初始化。因此initEnumToInt
在 foo::defaultIt
初始化之前运行。在您的代码中,这将导致未定义的行为,因为在initEnumToInt
运行点,foo::defaultIt
处于未初始化(但零初始化)状态;然后,您在零初始化对象上调用operator=
,然后调用期望零或未初始化对象的构造函数。
答案 1 :(得分:2)
你编写它的方式是,你正在访问一个未初始化的元素,因为sqlenumToInt
的初始化器首先被评估;这可能是未定义的行为(取决于迭代器类型的细节)。
如果您想要地图的正面,请在初始化程序中说出defaultIt = sqlenumToInt.begin()
,然后将其从initEnumToInt()
中删除。
(此外,即使你在函数中获得的迭代器也没有意义,因为一旦局部地图对象被破坏它就会失效。)
答案 2 :(得分:1)
文件范围变量按其定义的顺序初始化。在示例代码中,sqlenumToInt
将首先被初始化,调用initEnumToInt
,它将defaultIt
设置为在函数调用结束时变为无效的迭代器值(它指向{{1} },被摧毁; test
获得sqlenumToInt
的副本。然后test
的显式初始化开始,存储默认构造的迭代器。
答案 3 :(得分:0)
std::map<std::string, int>::iterator();
此行为map<string, int>
构建默认迭代器,因此您的defaultIt
将只是此默认迭代器的副本。如果您想要第一个地图元素,则应使用sqlenumToInt.begin()
初始化它。
对于初始化的顺序,在一个编译单元中,静态变量的初始化顺序与定义它们的顺序相同,但不同单元之间的顺序是未定义的。
答案 4 :(得分:0)
initEnumToInt()
内的这一行有问题:
defaultIt = test.insert(std::make_pair("a", 0)).first
此代码有两个问题。第一个是因为在到达行之前没有构造迭代器,它会导致未定义的行为如果迭代器没有一个简单的构造函数(调用{{1}在未初始化的对象上 - 这在某些情况下不是问题,但代码不可移植。)
该行的第二个问题是您正在设置迭代器以引用 local 映射中的元素。在函数完成后对该迭代器的任何使用都将是未定义的行为。
请注意,在函数内部设置迭代器不会向代码添加任何值,因此您可以将setter放在一边。如果您想要的是引用该元素的迭代器,您可以执行多项操作:如果始终是第一个元素,则在其初始化程序中将其设置为operator=
,如果您愿意引用地图中的特定元素(在初始化地点已知,将其设置为sqlenumToInt.begin()
。如果要将其设置为仅在sqlenumToInt.find(element)
函数内部已知的特定元素,则更改初始化的顺序,以便首先初始化迭代器并通过引用将其作为参数传递给initEnumToInt
函数。 - 这不是必需的,作为公共静态变量,函数无论如何都可以访问它,但是传递它引用使得依赖性以及在代码中显式函数中修改它的事实。
答案 5 :(得分:0)
初始化是在@ecatmur写的时候逐行执行的。所以:
为了解释它,我写了一个简单的例子:
// foo.h
#pragma once
#include <iostream>
struct bar
{
int value_;
bar(int value)
{
std::cout << "bar()\n";
set(value, true);
}
void set(int value, bool fromConstructor = false)
{
std::cout << ((fromConstructor) ? "Set from ctor" : "Set from non-ctor")
<< ", old value is: " << value_ << "\n";
value_ = value;
}
};
struct foo
{
static bar bar_;
static bool init()
{
std::cout << "init begin\n";
bar_.set(1);
std::cout << "init end\n";
return true;
}
};
// main.cpp
#include "foo.h"
bool b = foo::init();
bar foo::bar_ = bar(2);
int main()
{
std::cout << foo::bar_.value_ << "\n";
return 0;
}
输出是:
init begin
Set from non-ctor, old value is: 0
init end
bar()
Set from ctor, old value is: 1
2
因此,分配静态变量的内存,但稍后执行初始化,类似&#34; placement new&#34;机械。您可以在输出中看到,在init之后调用bar的c,但旧值为1(将由init方法设置)并且由于静态初始化而覆盖为2。
因此,您可以通过更改顺序静态初始化轻松解决此问题。但是你有另一个问题由@Kerrek SB描述:
(而且,即使你在函数中获得的迭代器也会 没有意义,因为它一旦局部地图就变得无效 对象被破坏。)
纠正您案件的变体之一:
class foo
{
typedef std::map<std::string, int> Map;
static bool initEnumToInt();
static Map sqlenumToInt;
static Map::iterator defaultIt;
static bool inited;
};
foo::Map foo::sqlenumToInt;
foo::Map::iterator defaultIt = foo::Map::iterator();
bool foo::sqlenumToInt = initEnumToInt();
bool foo::initEnumToInt();
{
defaultIt = sqlenumToInt.insert(std::make_pair("a", 0)).first;
sqlenumToInt["b"] = 2;
sqlenumToInt["c"] = 3;
sqlenumToInt["d"] = 4;
return true;
}