2014-02-24 71 views
1

我遇到了一个我认为与创建对象副本有关的问题。我有一个映射类,它包含一个std :: map,其中有一些类定义由它们的ID索引的不同tile类型,以及向量中的一堆tile,每个tile都有一个指向其中一个类的指针。此外,我还有另一个课程Scenario,其中包含地图。复制后访问对象时C++程序崩溃

当我创建一个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; 
} 

打印()在场景构造结束正确打印地图的内容,但该方案后,一个是分配导致崩溃。任何帮助,将不胜感激。

+1

这是很多代码,你没有正确实现拷贝构造函数,没有检查空指针和各种*坏事*。你应该使用一个调试器来调试你的代码,它应该揭示出现问题的地方...... – Nim

+0

'场景* newScenario = new Scenario(newMap);'''newScenario-> map.print();'请尝试。 –

+0

没有看到任何错误。在这里添加了你的代码.http://ideone.com/ivVFwd – shivakumar

回答

2

原来的std ::地图,你原来的TileInfo对象存储是在这里:

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,它建立了你的向量地图,这个向量地址为rtrnVal中相对的TileInfo对象。此代码:

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对象均rtrnValcreateMap

可能的解决方案

而不是具有地图一个包含指向TileInfo小号矢量,考虑地图索引矢量,其中每个索引关键字到基于0时隙在一个TileInfostd::vector那包含在地图的旁边。我可以看到你想要做什么(在多个单元格中共享单数TileInfo),所以你仍然可以获得这种好处。无论地图在哪里,矢量都会出现(它们拥有相同的Map对象),并且复制不会损害任何东西,因为(索引)映射和矢量(TileInfo)都将安全地复制。最后你仍然可以得到你想要的(单数TileInfo s),但现在用数字而不是指针索引。作为奖励,您不必在定制拷贝中处理从Tile Map到另一个Map的折叠指针。它的所有相对(0 .. n-1)

祝你好运。

+0

谢谢,我可能会这样做,但我认为复制时会调用Map(const Map&other)构造函数。我的逻辑是,它会将tileInfo映射复制到新对象,然后通过引用该构造函数来将它们填充到新的tileInfo映射中:Tile(const Tile&other,map &info)。它没有按照我的意图工作,所以我可能只是使用索引。 – jocamar

2

你存储指向TileInfo

class Tile 
{ 
public: 
    TileInfo* info; 
    ... 

但是一旦对象被复制时,指针跨越复制,但它们指向的TileInfo一直遭到破坏,因此他们正在引用无效的内存=崩溃。

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

+0

我已经实现了Map的拷贝构造函数,但我忘了实现赋值操作符,也许这就是为什么它失败了,我会回家时尝试。 – jocamar