从向量中删除对象会破坏 C++ 中另一个对象中的对象

Posted

技术标签:

【中文标题】从向量中删除对象会破坏 C++ 中另一个对象中的对象【英文标题】:Removing object from vector corrupts object in another in C++ 【发布时间】:2011-08-04 19:24:17 【问题描述】:

在过去的几天里,我一直在编写一个系统来简化基于文本的冒险的编写(类似于 Colossal Cave 或 Zork)。我遇到了 Item 子系统的问题。

相关的三个类是 Room、Door 和 Item。

当我从房间的项目向量中删除项目对象时,同一房间的门向量中的最后一个门对象已损坏。应该注意的是,所有的 Door 和 Item 对象都是通过引用传递的,这允许我们编写自己的 Door 和 Item 子类,并且仍然可以在系统中使用它们。奇怪的是,要损坏的门对象的唯一属性是“位置”属性,它告诉玩家门在房间的哪个位置。所有其他属性都保持不变,这告诉我指向门对象的指针没有移动,但它指向的数据已经改变。

这会导致玩家看到门描述的方式出现错误。为了测试,我没有给门特殊的描述,它们太多了,所以他们使用我的内部,罐头描述。

You are in a large parlour room.  It is obviously that of a very wealthy man.
Adorning the walls are many dusty heads of big game.
There is a door in the direction of West.
There is a door in the direction of East.
There is a door in the direction of North.
Hanging on the wall, you see a really bad ass looking sword.
Parlour] pickup sword
Picked up the Sword of the Gods
You pick up the sword.
In doing so, you feel very powerful.
There is a door in the direction of West.
There is a door in the direction of East.
There is a door in the direction of ?.
Parlour] 

玩家仍然可以从一个房间移动到另一个房间,所以门对象中的其余数据似乎没问题。但是,存储“Location”属性的 std::string 已更改。我没有在代码中更改该属性。

谁能明白为什么会这样?我的代码或对象处理中是否有明显的错误?因为我无法确定这是在哪个文件中发生的,所以有很多代码要发布。

您可以在这里下载我所有代码的 zip 文件:http://www.filedropper.com/advsys

这是我的代码,这当然不是全部,因为我在 9 个文件中有 467 行 当你试图拿起剑时调用它。

void OnPickup() 
    std::cout << "You pick up the sword." << std::endl;
    std::cout << "In doing so, you feel very powerful." << std::endl;

    int itemNum = ParentRoom->HasItem(Name);
    if (itemNum != -1) 
        // Wait, the item ISN'T in the room?  Then how the hell did we get HERE?
        // Whatever, error out.
        std::cout << "For some strange reason, you were hallucinating about the " + Name + "." << std::endl;
     else 
        PlayerRef->Inventory.push_back(new GodSword(ParentRoom, PlayerRef));
        ParentRoom->RemoveItem(itemNum);  // Corrupts a door
        //delete ParentRoom->Items[itemNum];  // SEGFAULT
    

门和物品是这样分配的。 一个新的 Door 实例被传递了文本位置、它在房间内的位置以及指向目的地的指针。项目被传递一个对它们所在房间的引用,以及一个对玩家的引用。

house["parlour"].Doors.push_back(new Door("North", 'N', &house["bedroom"]));
house["parlour"].Items.push_back(new GodSword(&house["parlour"], &player));

像这样从房间中删除项目。打印门列表以进行调试。

void Room::RemoveItem(int item) 
    Items.erase(Items.begin() + item);

     for (int i = 0; i < Doors.size(); i++) 
         std::cout << Doors[i]->GetDescription() << std::endl;
     

房间声明如下。

class Room 
    public:
        std::vector<Door *> Doors;
        std::vector<Item *> Items;

        std::string LongDescription;
        std::string ShortDescription;
        std::string Name;

        bool DescribeDoors;
        bool DescribeItems;
        bool BeenHere;

        Room();
        ~Room();

        int HasDoor(char dir);
        int HasItem(std::string name);
        void OutputDescription(bool forceLong=false);
        void RemoveItem(int item);
;

我想不出还有什么需要补充的。如果您需要查看另一个文件,或者如何声明不同的类等...上面有一个 zip 的链接。

【问题讨论】:

我最近遇到了与std::map 类似的问题,导致了段错误。问题是由在std::map&lt;&gt;::iterator 循环中调用erase()gmtime_r() 引起的,这似乎破坏了地图,即使gmtime_r() 没有指向地图中的任何内容。删除 erase() gmtime_r() 可以防止任何问题。由于我的截止日期很短,所以我没有在此处发布,并通过在条目中设置标志而不是调用erase() 来解决(延迟?)问题。我在第一个迭代器循环之后添加了另一个迭代器循环,它查找标记的条目并调用erase() 这可能无关,但我在这里有点迷失; Room::HasItem 本质上是一个 IndexOf 操作,但是您在示例代码中存在的条件是 if(itemNum != -1) 然后找不到它。也许我的眼睛只是模糊了,但这是正确的吗,如果找不到该项目,您似乎只会点击您的 RemoveItem 块,在这种情况下您将调用 Items.erase(Items.begin() + (-1)); 使用指针并在堆上手动分配的原因是什么?让容器管理您的内存,您可能希望使用std::list 来更有效地插入和删除。附言我相信 Quintin 解决了你的问题。 【参考方案1】:

在您发布的这段代码中:

   int itemNum = ParentRoom->HasItem(Name);
    if (itemNum != -1) 
        // Wait, the item ISN'T in the room?  Then how the hell did we get HERE?
        // Whatever, error out.
        std::cout << "For some strange reason, you were hallucinating about the " + Name + "." << std::endl;
     else 
        PlayerRef->Inventory.push_back(new GodSword(ParentRoom, PlayerRef));
        ParentRoom->RemoveItem(itemNum);  // Corrupts a door
        //delete ParentRoom->Items[itemNum];  // SEGFAULT
    

当您尝试删除它时,itemNum 将为 -1,这可能会导致您注意到的段错误/内存损坏。测试的意义是否与应有的相反?

【讨论】:

+1 哦,你的速度更快 :) 另外,作为旁注,ZIP 文件中的代码无法编译 - 每个 cpp 文件中都缺少包含文件(主要是 标头)。如果你想要一个可移植的代码,请添加它们。 谢谢你,做到了。我也会添加所需的标题。【参考方案2】:

Dewtell 发现了导致内存损坏的错误。然而,代码中真正的错误在于这一行:

int itemNum = ParentRoom->HasItem(Name);

应该是这样的:

int itemNum = ParentRoom->HasItem(PickupName);

否则永远找不到匹配项。

【讨论】:

以上是关于从向量中删除对象会破坏 C++ 中另一个对象中的对象的主要内容,如果未能解决你的问题,请参考以下文章

如何从 SwiftUI 和 Realm 中另一个列表中的对象中添加和删除列表中的对象

c ++在析构函数中删除向量类成员内存

如何从 C++ 中另一个向量的子元素创建一个向量?

iOS 从超级视图中删除对象会破坏自动布局并禁用滚动视图

如何从 C++ 中的“指向对象的指针”向量访问对象

使用迭代器 C++ 删除对象类型的向量元素