提问人:Mr.C64 提问时间:9/5/2023 最后编辑:Mr.C64 更新时间:9/5/2023 访问量:112
静态 std::map 成员变量在 C++ 中安全吗?
Are static std::map member variables safe in C++?
问:
假设我想实现一种查找表,例如:std::map<std::string, int>
// Utils.hpp header
class Utils
{
public:
static std::map<std::string, int> NameToId;
static int GetIdForName(const std::string & name)
{
auto it = NameToId.find(name);
if (it != NameToId.end())
return it->second;
else
return -1; // not found
}
};
在相应的 .cpp 文件中:
// Utils.cpp
#include “Utils.hpp”
std::map<std::string, int> Utils::NameToId = {
{“foo”, 10},
{“bar”, 20},
…
};
有这样的成员变量安全吗?static std::map
这会导致由于(和)动态初始化而导致细微的错误吗?std::map
std::string
如果该表在编译时是已知的并且是只读的,那么用类似 的东西替换 of 的使用并使用线性搜索(如果对按字符串键排序,则使用二进制搜索)来查找与字符串关联的整数值会更好/更安全吗?std::map
std::array<std::pair<std::string_view, int>>
答:
这是我在 redis 客户端中修复的一个错误:
客户端类可以创建订阅者对象(您需要一个客户端对象来创建订阅者):
class Client {
public:
Subscriber subscriber() { ... }
};
订阅者包含这样一个(无序但相同)静态映射:
class Subscriber {
public:
static std::map<...> table;
}
现在,当您将两件事结合起来时,乐趣就开始了:
- 创建一个静态全局客户端(我没有说遵循最佳实践会发生错误)
- 确保该类的某个方法用于进行一些查找(因为我们不喜欢 和 )。好吧,实际上它使用静态方法,并使用静态成员,但无论如何......
Client
Subscriber::table
switch
if else
Subscriber
按初始化-销毁顺序排列的是
- 创建客户端
- 创建订阅者
- 销毁订阅者
- 销毁客户端
在步骤 3 和 4 之间,客户端可能有一些未处理的消息队列,当它处理它们时,它将不得不使用一个死的静态属性。客户端和静态成员都位于全局空间中,我们只是碰巧在订阅者静态成员之前初始化客户端。
再加上一个客户端可能有多个订阅者并行运行的事实(公平地说,在处理东西之前有一个线程在运行,所以客户端对象必须在析构函数完成之前处理剩余的数据 - 如果没有异步计算延迟析构函数,则该错误是不可观察的),并且您有一种非常好的方式来放松对清理程序报告的思念。享受
结论:你所拥有的不是防弹的。在类外如何使用或访问该静态内容时要非常小心。如果只有非静态方法使用它,我上面描述的错误就不会发生。如果公共静态方法使用它,或者如果成员本身是公共的,则要怀疑它如何与其他静态对象混合。
评论
std::array
switch
评论
main
NameToId
const