在调试了一些非常奇怪的行为后,比如向量从0大小突然变成负值,我意识到这是在用new分配4000 int后发生的。我不明白,4k int(~15KB)并没有那么大,即使是,为什么它会干扰我的内存?视频:https://drive.google.com/file/d/0B_iN9pcbyoiKQV9lNllLWktfOGc/edit?usp=sharing
在视频中,你看到的结构是这样的:
struct TileMap{
int * pTileMap;
int mapW, mapH;
float hSpacing, vSpacing;
sprite::InstancedSprites m_tileInstSprites;
sprite::InstancesVertexBuffer m_IVB;
TileMap():pTileMap(nullptr){}
~TileMap(){ if(pTileMap) delete [] pTileMap; }
};
std::vector<TileMap> m_vTileMaps;
编辑
所以,即使注释掉了分配,几步后也会发生同样的事情,我想是前一刻的覆盖吗?有关于如何调试的提示吗?
编辑2
问题与指针无关,问题出在这里:
sprite::InstancedSprites::InstancedSprites()
:
m_drawCall(6),
m_drawCall_warpException(6)
{
m_drawable.AddPipelineState( &m_pipeState );
m_drawable.SetDrawCall( &m_drawCall );
m_drawable_warpException.AddPipelineState( &m_pipeState );
m_drawable_warpException.SetDrawCall( &m_drawCall_warpException );
}
这就是ctor,我认为它应该做得很好,drawable中的所有内容都是std::vector(AddPipelineState正在向向量添加)。
我"解决"它的方法是添加与ctor在这种方法中所做的相同的工作:
void sprite::InstancedSprites::Initialize( dx::BindPSShaderResourceView & pTextureBinder_p, dx::BindOMBlendState & pBlendBinder, dx::BindPSSampler & pSampleState, InstancesVertexBuffer & pIVB_p )
{
// DBG
m_drawable.Clear();
m_drawable.AddPipelineState( &m_pipeState );
m_drawable.SetDrawCall( &m_drawCall );
m_drawable_warpException.Clear();
m_drawable_warpException.AddPipelineState( &m_pipeState );
m_drawable_warpException.SetDrawCall( &m_drawCall_warpException );
m_pipeState.Reset();
m_pipeState.AddBinderCommand( &pTextureBinder_p );
m_pipeState.AddBinderCommand( &pBlendBinder );
m_pipeState.AddBinderCommand( &pSampleState );
m_pipeState.AddBinderCommand( pIVB_p.GetBinder() );
// missing, IA and camera binds
m_pIVBref = &pIVB_p;
}
上半部分是修复,我没有(我有方法),因为ctor已经做了。。。所以我真的不明白。如果内部dravable使用向量,那么这不可能是三的规则吗?
编辑3
最终的解决方案是,一个根本不复制的cpy ctor,问题是,这些dratables将其保存在所有者类上的数据上,因此在复制时,它们在获取带有数据的向量时不再有效…:
sprite::InstancedSprites::InstancedSprites( InstancedSprites & /*other_p*/ )
:
m_drawCall(6),
m_drawCall_warpException(6)
{
m_drawable.AddPipelineState( &m_pipeState );
m_drawable.SetDrawCall( &m_drawCall );
m_drawable_warpException.AddPipelineState( &m_pipeState );
m_drawable_warpException.SetDrawCall( &m_drawCall_warpException );
}
std::vector<TileMap> m_vTileMaps;
您正在创建TileMap对象的矢量。TileMap违反了三规则,任何违反该规则的类型都不能在向量中安全使用。换句话说,这个简单的类测试显示了将要发生的问题:
int main()
{
TileMap x;
// assume x has allocated the ints
//..
TileMap y = x; // trouble, trouble
TileMap z;
//.. assume z has allocated ints
z = x; // more trouble
} // double deletion error and memory leak on program exit
上面的代码不使用矢量,但显示了矢量内部将执行的操作类型。您可以看到在制作副本时出现的问题。
矢量类将创建TileMap的副本,并且TileMap对象不可安全复制。您需要添加一个用户定义的复制构造函数、赋值运算符和析构函数(您提供的),以使类正确复制。
另一种解决方案是存储指向TileMap的指针向量(最好是智能指针)。
std::vector<TileMap*> m_vTileMaps;
m_vTileMaps.reserve(4000);
m_vTileMaps.push_back(new TileMap());
然后在释放内存时:
for(auto it=m_vTileMaps.begin(); it!=m_vTileMaps.end(); ++it)
{
delete(*it);
}
m_vTileMaps.clear();
或者通过使用shared_ptr,您可以忘记最后一部分。
另一个解决方案是在TileMap中使用矢量。你已经在使用矢量了,但你拒绝在它真正重要的地方使用它:
struct TileMap
{
std::vector<int> pTileMap;
int mapW, mapH;
float hSpacing, vSpacing;
sprite::InstancedSprites m_tileInstSprites;
sprite::InstancesVertexBuffer m_IVB;
};
std::vector<TileMap> m_vTileMaps; // this is ok now
TileMap类现在可以在向量中安全地使用(我假设sprite::*类型可以安全地复制)。您也不再需要在析构函数中执行delete[]操作。