C++ unordered_map 初始化读取访问冲突
Posted
技术标签:
【中文标题】C++ unordered_map 初始化读取访问冲突【英文标题】:C++ unordered_map initialization read access violation 【发布时间】:2020-08-24 11:50:31 【问题描述】:我有一个结构:
struct Foo
std::unordered_map<std::string, std::string> info;
int count;
int bar;
我正在尝试按如下方式在堆上初始化此结构:
Foo* createFoo(int count, int bar)
Foo* foo = (Foo*)malloc(sizeof(Foo));
foo->info = std::unordered_map<std::string, std::string>(); // <- exception thrown here
foo->count = count;
foo->bar = bar;
return foo;
我在构造 unordered_map
时遇到以下异常:
Exception thrown: read access violation. _Pnext was 0xCDCDCDD1.
我知道 MVS 用 0xCD
填充堆分配的内存,这就是为什么 _Pnext
指针有这个值,但我不明白为什么 unordered_map
构造函数没有将这些字段归零。
我意识到现代 C++ 执行此操作的方式是使用 new
/constructors,但我正在尝试使用(基本上)POD 对象以非 OOP 程序方式编写此代码。
我是不是初始化地图不正确?
【问题讨论】:
Aunordered_map
绝不是 POD 类型。既然知道应该怎么做,为什么还要这样做?
使用new
不是“现代”方式;自从 C++ 被称为“C with classes”以来,它一直是的方式。它也与 OOP 完全无关。
@chips C++ 是多范式语言,OOP 是它支持的范式之一。这与初始化std::unordered_map
无关,您在初始化Foo
时遇到了问题。 Foo
的初始化将初始化其所有成员。
我可以推荐一个好的C++ Book。将节省大量时间。
我是不是初始化地图不正确?你根本没有初始化地图。
【参考方案1】:
malloc()
不初始化分配的内存,这在分配具有非平凡构造函数的对象时很糟糕。
您应该改用new
。
Foo* foo = new Foo;
要解除分配通过new
分配的对象,您可以使用delete
。
delete pointe_to_object;
【讨论】:
【参考方案2】:malloc 的 C 调用
Foo* foo = (Foo*)malloc(sizeof(Foo));
不为数据成员调用构造函数。
所以数据成员
std::unordered_map<std::string, std::string> info;
没有构建。
这个语句带有复制赋值运算符
foo->info = std::unordered_map<std::string, std::string>();
导致未定义的行为,因为没有创建对象foo->info
。
您必须使用运算符 new 而不是 malloc。
例如
Foo* foo = new Foo();
【讨论】:
有没有办法手动调用原地的unordered_map构造函数? @chips 你为什么要寻找非标准的方法来分配一个对象? @chips 你可以像new(&foo->info) std::unordered_map<std::string, std::string>;
一样使用placement new
@MikeCAT 这就是我问题的答案 - 谢谢【参考方案3】:
malloc()
不创建任何对象。它只是保留一些未初始化的内存。该内存中没有对象,必须使用位置 new
创建这些对象。
现在,operator=
需要一个现有对象,因为它是成员函数(始终)。通过在foo->info = std::unordered_map<std::string, std::string>()
行上调用此运算符,您可以在不存在的对象上调用运算符。
解决方案是不要违背语言并使用new
,因为你应该这样做:
Foo* createFoo(int count, int bar)
Foo* foo = new Foo;
// unnecessary now, the object is already constructed and default-initialized
// foo->info = std::unordered_map<std::string, std::string>();
// ints are constructed, but not initialized
foo->count = count;
foo->bar = bar;
return foo;
您也可以将malloc
与放置new
一起使用,但这仅在您需要没有实际对象的内存时才有用(例如在向量实现中)。
注意:在现代 C++ 中使用原始的 new
是一种不好的气味(嗯,9 岁,但与 std::unordered_map
一样现代)。请改用 smart pointers 和 STL 容器。
【讨论】:
据我所知,所有 new 所做的都是分配内存,然后调用默认构造函数(这不正确吗?)如果确实如此,那么我仍然不明白为什么默认构造函数可以'不能就地手动调用...... @chips 不正确。您正在描述new
的行为,malloc
仅分配内存,仅此而已。您可以使用放置new
手动调用构造函数,如其他答案下的评论中所述。但是您正在尝试调用operator=
,而不仅仅是默认构造函数,这是UB。
我了解 malloc 的作用。我刚刚注意到位置新评论回答了我的问题。如果 operator= 是 UB,则不确定为什么我的编译器不叫我各种名字。
UB 的意思是“任何事情都可能发生”。崩溃(如使用未初始化的非 POD 结构时)或看似正常工作(如使用未初始化的int
时)都是 UB 的完美示例。如果编译器必须指出 UB,它就不再是 UB。以上是关于C++ unordered_map 初始化读取访问冲突的主要内容,如果未能解决你的问题,请参考以下文章
如何在 C++ 中的 unordered_map 的并发读取和锁定单线程写入之间交替