复制后访问对象时C ++程序崩溃

时间:2014-02-24 11:20:19

标签: c++ pointers deep-copy

我遇到的问题与创建对象副本有关。我有一个map类,它包含一个std :: map,其中包含一组定义由其ID索引的不同tile类型的类,以及一个向量中的一堆tile,每个tile都有一个指向其中一个类的指针。另外,我还有另一个包含地图的类Scenario。

当我创建一个Scenario对象并尝试对其地图执行任何操作时,问题就出现了。复制指针时一定有问题,但我无法弄明白。

我试图将代码减少到重现问题的必要条件:

#include <string>
#include <map>
#include <vector>
#include <iostream>

using namespace std;

class TileInfo {
    string name;

public:
    int id;
    TileInfo() {};
    TileInfo(int id, string name);

    string getName() const;
};

TileInfo::TileInfo(int id, string name)
{
    this->id = id;
    this->name = name;
}

string TileInfo::getName() const 
{
    return name;
}

class Tile
{
public:
    TileInfo* info;

    Tile() {};
    Tile(const Tile & other, map<int,TileInfo> & info);
    Tile (TileInfo* info);

    string getName() const;
};

Tile::Tile (TileInfo* tileInfo)
{
    this->info = tileInfo;
}

Tile::Tile( const Tile & other, map<int,TileInfo> & tileInfo )
{
    map<int,TileInfo>::iterator it = tileInfo.find(other.info->id);

    if(it == tileInfo.end())
        this->info = NULL;
    else
        this->info = &(it->second);
}

string Tile::getName() const 
{
    return info->getName();
}

class Map
{
    vector <vector <Tile>> tiles;
    map <int, TileInfo> tileInfo;

public:
    void print() const;

    Map() {};
    Map(map<int,TileInfo> tileInfo);
    Map(const Map & other);

    string getName() const;
    void loadTiles();
};

Map::Map(map<int,TileInfo> tileInfo)
{
    this->tileInfo = tileInfo;
}

Map::Map(const Map & other)
{
    this->tileInfo = other.tileInfo;

    for(unsigned int i = 0; i < other.tiles.size(); i++)
    {
        vector<Tile> line;

        for(unsigned int j = 0; j < other.tiles[i].size(); j++)
        {
            Tile tmpTile = Tile(other.tiles[i][j],tileInfo);
            line.push_back(tmpTile);
        }

        this->tiles.push_back(line);
    }
}

void Map::print() const
{
    for(unsigned int i = 0; i < tiles.size(); i++)
    {
        for(unsigned int j = 0; j < tiles[i].size(); j++)
        {
            cout << "Tile at " << i << ", " << j << " is a "; cout << tiles[i][j].getName() << endl;
        }
    }
}

void Map::loadTiles()
{
    for(unsigned int i = 0; i < 3; i++)
    {
        vector <Tile> line;
        for(unsigned int j = 0; j < 3; j++)
        {
            map<int,TileInfo>::iterator ite = tileInfo.find(i);
            if( ite!=tileInfo.end())
                line.push_back(Tile(&(ite->second)));
            else
            {
                line.push_back(Tile(NULL));
            }
        }

        tiles.push_back(line);
    }
}

class Scenario
{
public:
    Map map;
    Scenario() {};
    Scenario(Map map);
};

Scenario::Scenario(Map map)
{
    this->map = map;
    this->map.print();
}

Map createMap()
{
    map<int,TileInfo> tmpInfo;

    for(unsigned int i = 0; i < 3; i++)
    {
        tmpInfo.insert(pair<int,TileInfo>(i,TileInfo(i,"testname")));
    }

    Map rtrnVal = Map(tmpInfo);

    rtrnVal.loadTiles();

    return rtrnVal;
}

int main()
{
    Map newMap = createMap();

    Scenario newScenario = Scenario(newMap);
    newScenario.map.print();
    int input;
    cin >> input;
}

Scenario构造函数末尾的print()正确打印地图的内容,但是在分配方案后的那个会导致崩溃。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:2)

您正在存储指向TileInfo

的指针
class Tile
{
public:
    TileInfo* info;
    ...

但是一旦复制了对象,指针就会被复制,但是它们指向的TileInfo已被破坏,所以它们引用了无效的内存=崩溃。

考虑实现复制构造函数/复制操作符来处理它。

答案 1 :(得分:2)

存储原始TileInfo对象的原始std :: map位于:

map<int,TileInfo> tmpInfo;

for(unsigned int i = 0; i < 3; i++)
{
    tmpInfo.insert(pair<int,TileInfo>(i,TileInfo(i,"testname")));
}

这是房子对的地图。然后你复制一下所说的地图。它会创建所有TileInfo个对象的副本,这没关系。

Map rtrnVal = Map(tmpInfo);

但这是车轮开始脱落的地方。您在此处触发loadTiles,这会设置您的地图,这些地图会在TileInfo中添加相对rtrnVal个对象。这段代码:

rtrnVal.loadTiles();

导致此代码。请注意,您将映射的TileInfo对象的地址推送到向量中。这些驻留的地图是对象地图。

void Map::loadTiles()
{
    for(unsigned int i = 0; i < 3; i++)
    {
        vector <Tile> line;
        for(unsigned int j = 0; j < 3; j++)
        {
            map<int,TileInfo>::iterator ite = tileInfo.find(i);
            if( ite!=tileInfo.end())
                line.push_back(Tile(&(ite->second)));
            else
            {
                line.push_back(Tile(NULL));
            }
        }

        tiles.push_back(line);
    }
}

最后,回到我们开始的地方你做到了:

return rtrnVal;

这会制作Map 的副本,该副本会制作向量地图的副本,其中包含指向TileInfo内地图中rtrnVal的指针即将在函数退出时销毁。结果回到了呼叫方。

Map newMap = createMap(); 

newMap现在暂挂指向TileInfo rtrnVal createMapTileInfo个对象的悬挂指针。

可能的解决方案

不要使用包含指向TileInfo的指针的向量映射,而是考虑索引的向量映射,其中每个索引键入{{1}中基于0的插槽地图旁边包含的std::vector。我可以看到你想要做什么(单个TileInfo在多个单元格中共享),所以你仍然可以获得这些好处。无论地图在哪里(它们都由相同的Map对象拥有),向量都会出现,并且复制不会对任何东西造成伤害,因为地图(索引)和向量({{1} }}将安全复制。最后你仍然得到你想要的东西(单数TileInfo s)但现在用数字而不是指针索引。作为奖励,您不必在自定义副本中处理从一个映射到另一个映射的指向TileInfo的指针。它的所有相对(0 .. n-1)

祝你好运。