关于用vector管理CImage时遇到的坑

来源:互联网 发布:上杉升 知乎 编辑:程序博客网 时间:2024/06/15 17:11

假设有一个类A,里面有一个CImage。如果用vector储存,erase前面的元素后,后面的CImage就无法使用,会报ATLASSERT( hBitmap == m_hBitmap );检查失败

struct A{    CImage img;};int _tmain(int argc, _TCHAR* argv[]){    vector<A> imgs(2);    imgs[0].img.Create(1, 1, 32);    imgs[1].img.Create(1, 1, 32);    // 正常工作    imgs[0].img.GetDC();    imgs[0].img.ReleaseDC();    imgs[1].img.GetDC();    imgs[1].img.ReleaseDC();    imgs.erase(imgs.begin());      // 删除第一个img    imgs[0].img.GetDC();    imgs[0].img.ReleaseDC();       // 出错,ATLASSERT( hBitmap == m_hBitmap );检查失败    return 0;}

GetDC和ReleaseDC是这样的

inline HDC CImage::GetDC() const throw(){    ATLASSUME( m_hBitmap != NULL );    m_nDCRefCount++;    if( m_hDC == NULL )    {        m_hDC = GetCDCCacheInstance()->GetDC();        m_hOldBitmap = HBITMAP( ::SelectObject( m_hDC, m_hBitmap ) );    }    return( m_hDC );}inline void CImage::ReleaseDC() const throw(){    HBITMAP hBitmap;    ATLASSUME( m_hDC != NULL );    m_nDCRefCount--;    if( m_nDCRefCount == 0 )    {        hBitmap = HBITMAP( ::SelectObject( m_hDC, m_hOldBitmap ) );        ATLASSERT( hBitmap == m_hBitmap );        GetCDCCacheInstance()->ReleaseDC( m_hDC );        m_hDC = NULL;    }}

调试看了一下,ReleaseDC里的SelectObject返回值是NULL,更神奇的是上一句的GetDC里的SelectObject返回值也是NULL。看了一下SelectObject的文档,当调用失败时才返回NULL,而且一张BITMAP不能选入多个DC中。不过这里的BITMAP也没有选入多个DC啊,想了半天也没想出什么错误,直到我偶然调试到CImage的析构函数时发现,vector中的两个img的hBitmap居然是一样的

vector内存

仔细想想,应该是vector删除前面元素时需要把后面的元素往前移动,然后不小心把后面的元素拷贝了一份,然后在后面元素的析构函数中把hBitmap释放了。查看erase的实现证实了我的想法

        // TEMPLATE FUNCTION movetemplate<class _InIt,    class _OutIt> inline    _OutIt _Move(_InIt _First, _InIt _Last,        _OutIt _Dest, _Nonscalar_ptr_iterator_tag)    {   // move [_First, _Last) to [_Dest, ...), arbitrary iterators    for (; _First != _Last; ++_Dest, ++_First)        *_Dest = _STD move(*_First);  // 由于CImage没有实现移动的=操作符,这里调用了默认的拷贝的=操作符    return (_Dest);    }

解决方法:

  1. 自己实现移动的=操作符

    struct A{  A& operator= (A&& other)  {      img.Destroy();      img.Attach(other.img.Detach());      return *this;  }  CImage img;};
  2. 用指针管理CImage

    int _tmain(int argc, _TCHAR* argv[]){  vector<unique_ptr<A> > imgs;  imgs.push_back(make_unique<A>());  imgs.push_back(make_unique<A>());  imgs[0]->img.Create(1, 1, 32);  imgs[1]->img.Create(1, 1, 32);  // 正常工作  imgs.erase(imgs.begin());  imgs[0]->img.GetDC();  imgs[0]->img.ReleaseDC();  return 0;}
原创粉丝点击