从映射中检索后,映射中的对象已损坏

Object in map corrupted after retrieving from the map

本文关键字:映射 对象 已损坏 检索      更新时间:2023-10-16

所以我已经完全编辑了我的问题。我有一个名为mTextMapmap,它包含:

typedef std::map<const std::string, Text*>      TextMap;
TextMap mTextMap;

我有以下方法:

void Foo::setUpGame()
{
    Text text(1,2,3,4); //create a Text object
    mTextMap["MainText"] = &text; //save it in the map!
}
Text& Foo::getText(const std::string name)
{
    auto i= mTextMap.find(name);
    return *(i->second); //Gets a reference to the Text that is inside the map
}

现在,如果我使用这种方式:

Foo foo;
foo.setUpGame();
Text& myText = foo.getText("MainText"); // Why is this corrupted?

对象myText已完全损坏!!

为什么会发生这种情况?

一般的问题似乎是,您认为这一行:

mTextMap["MainText"] = &text;

将文本对象存储在地图中。事实并非如此!它在映射中存储一个指向对象的指针,文本对象本身将在函数结束时自动销毁,正如您自己所说。因此,现在您的指针指向一个不存在的对象,这将导致观察到的错误。

你的问题有各种各样的解决方案,具体取决于你试图实现的目标以及你将在课堂上做什么。

一种可能性是使用Text对象的映射(而不是指针):

typedef std::map<const std::string, Text>      TextMap;
void Foo::setUpGame()
{
    Text text(1, 2, 3, 4); //create a Text object
    mTextMap["MainText"] = text; //copy it into the map!
}

void Foo::setUpGame()
{       
    mTextMap.emplace("MainText", Text(1, 2, 3, 4)); //Doesn't require Text to be default constructable
}

另一种可能性是在堆上创建文本对象并使用智能指针(例如unique_ptr)

typedef std::map<const std::string, std::unique_ptr<Text>>      TextMap;
void Foo::setUpGame()
{
    mTextMap["MainText"] = std::make_unique<Text>(1,2,3,4); //create object on the heap an store a pointer to it in the map
}

一旦地图被销毁,std::unique_ptr就会自动销毁文本对象。

如果出于某种原因,你真的需要一个原始指针的映射,你可以像David解释的那样使用"new",但当你不再使用它们时,不要忘记删除它们——c++没有垃圾收集器(比如java)可以自动处理这件事。

一旦setUpGame完成,"text"对象就会超出范围。在这一点上,堆内存被释放出来,以便被堆的任何新使用所覆盖。它本质上是一个临时的项目草稿,只存在于函数的范围内(或函数内的显式范围运算符内)。

David G的建议是合理的:阅读更多关于堆栈内存和堆内存之间的区别的信息,并考虑使用智能指针的建议。然而,如果你想要一个廉价、肮脏的解决方案来解决你眼前的问题,你可以试试这个:

void Foo::setUpGame()
{
    static Text text(1,2,3,4); // Note use of "static" keyword
    mTextMap["MainText"] = &text; //save it in the map!
}

虽然我不主张使用静态作为解决更基本的体系结构内存问题的捷径,但如果你迫切想让事情正常运行,你可以将其作为短期措施。将对象标记为静态可确保其生存时间超过函数的作用域。但我不建议将其作为这类问题的长期解决方案。

当您为对象动态分配内存时,只要您没有明确地将其从内存中删除,它就会一直存在,在您退出创建它的方法后,它不会被删除,所以您可以将指向它的指针放在映射中,它将一直存在(只需确保从映射中删除对象时删除内存)。

你可以用下面的简单代码来测试这一点,我在一个函数中声明一个新的Int,返回一个指向内存的指针,并将其打印到接收映射的另一个函数(其中有指针)中。它打印正确,这意味着即使超出范围,内存也没有被释放。

#include <iostream>
#include <map>
std::map<std::string, int*> myMap(){
    int* test = new int(1);
    std::map<std::string, int*> ObjMap;
    ObjMap["object"] = test;
    return ObjMap;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    std::map<std::string, int*> mmap = myMap();
    std::cout << *mmap["object"] << std::endl;
    return 0;
}

因此,要回答您的问题,请像这样动态创建您的对象:

Obj* obj = new obj(1,2,3,4);

当超出范围时,它不会被删除。尽管如此,除非您使用智能指针,否则您需要自己删除内存,例如:delete obj;(当您从映射中删除它时,释放内存,因为它不会自动释放)。

附言:您应该阅读堆栈和堆的工作原理,以及动态和静态分配的工作原理(使用堆栈或堆)。请参阅本c++动态内存分配教程以获得更多信息。

正如MikeMB所说,使用智能指针更容易,因为你可以确保删除了内存,也可以确保永远不会访问已删除的内存。有关智能指针的信息,请参阅此堆栈溢出主题:什么是智能指针,何时应该使用智能指针?