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 程序方式编写此代码。

我是不是初始化地图不正确?

【问题讨论】:

A unordered_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-&gt;info

您必须使用运算符 new 而不是 malloc。

例如

Foo* foo = new Foo();

【讨论】:

有没有办法手动调用原地的unordered_map构造函数? @chips 你为什么要寻找非标准的方法来分配一个对象? @chips 你可以像new(&amp;foo-&gt;info) std::unordered_map&lt;std::string, std::string&gt;;一样使用placement new @MikeCAT 这就是我问题的答案 - 谢谢【参考方案3】:

malloc() 不创建任何对象。它只是保留一些未初始化的内存。该内存中没有对象,必须使用位置 new 创建这些对象。

现在,operator= 需要一个现有对象,因为它是成员函数(始终)。通过在foo-&gt;info = std::unordered_map&lt;std::string, std::string&gt;() 行上调用此运算符,您可以在不存在的对象上调用运算符。

解决方案是不要违背语言并使用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 的并发读取和锁定单线程写入之间交替

不支持 NodeJS 插件 Unordered_map?

在 C++ 中从 unordered_map 访问值

C++ std::unordered_map怎么用

无法从分离的线程访问 C++ unordered_map 中的键值对

C++ stl unordered_map 单写(只插入,不删除)和单读并发访问