今天做贪食蛇的时候,当吃了几个食物的时候,系统报错,我是在group上画图,蛇头,蛇尾,蛇身,三张位图。
CreateCompatibleDC(NULL) 创建失败,网上查了原因,应该是内存不足引起的,查看任务管理器,果然发现内存一直增长。
经过排查原因发现是dc没有释放。
HDC memDc=CreateCompatibleDC(NULL);//这种的必须要释放 DeleteDC(memDc);//必须释放,要不占内存
但是 CDC sourceDC, destDC;
sourceDC.CreateCompatibleDC(NULL);
destDC.CreateCompatibleDC(NULL);
这种的就不用释放,内存不会增加,个人推测这种是包装过的类,应该是退出函数时,调用析构函数进行释放了。不需要显式释放。
类似的
CWnd *cw=GetDlgItem(IDC_GROUP_MAIN);
HDC hdc=cw->GetDC()->GetSafeHdc();
这种通过 HDC来接受的就需要 显式的调用释放函数 DeleteDC(hdc); cdc应该就不用
其中误解了很多地方占用内存没释放,事后发现其实没有,误解的地方:
cbitmap不用释放内存 和LoadBitmap 没有关系 和bitmap也没关系,不用手动释放
和还原原始设备也没关系 SelectObject(memDc,bitold);
和CArray<CPoint,CPoint&> arr_points; 没关系,他会自动释放,也不用removeall
另外。
个人觉得,这个是立刻释放内存的做法,这个应该不算内存泄露,因为debug的时候没有内存泄露的提示,而且,最小化后,再最大化,内存减了很多。
总结就是:指针和原始类需要手动释放内存,其他的不需要。
相关源代码如下:
// SnakeView.cpp : implementation of the CSnakeView class // #include "stdafx.h" #include "Snake.h" #include "SnakeDoc.h" #include "SnakeView.h" #include "GAMESET.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define TimerMain 1 int GetRand(double MAX,double MIN); HBITMAP GetRotatedBitmapNT( HBITMAP hBitmap, float radians, COLORREF clrBack); ///////////////////////////////////////////////////////////////////////////// // CSnakeView IMPLEMENT_DYNCREATE(CSnakeView, CFormView) BEGIN_MESSAGE_MAP(CSnakeView, CFormView) //{{AFX_MSG_MAP(CSnakeView) ON_WM_KEYDOWN() ON_WM_TIMER() ON_BN_CLICKED(IDC_BTN_START, OnBtnStart) ON_WM_SHOWWINDOW() ON_COMMAND(IDM_GAME_SET, OnGameSet) //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CFormView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CFormView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CFormView::OnFilePrintPreview) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CSnakeView construction/destruction CSnakeView::CSnakeView():CFormView(CSnakeView::IDD), m_iDircFlag(3),m_changeTail(FALSE),m_iTailDegree(0), m_pFood(CPoint(0,0)),iRandomSeed(0),i_Speed(0)//默认是向右 { // TODO: add construction code here } CSnakeView::~CSnakeView() { } BOOL CSnakeView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs return CFormView::PreCreateWindow(cs); } ///////////////////////////////////////////////////////////////////////////// // CSnakeView drawing void CSnakeView::OnDraw(CDC* pDC) { CSnakeDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here int size=m_snake.m_points.GetSize(); for (int i=0;i<size;i++) { CBitmap bit; //HBITMAP bitold; HBITMAP hbitmap; if (i==0||i==size-1)//表示是头或者尾巴 { if (i==0) { bit.LoadBitmap(IDB_SHead);//头 int degree; switch (m_iDircFlag) { case 0: degree=90; break; case 1: degree=-90; break; case 2: degree=180; break; case 3: degree=0; break; } hbitmap=(HBITMAP)GetRotatedBitmapNT(bit,degree*3.14159265359/180,pDC->GetBkColor());//旋转图片 bit.Detach(); bit.Attach(hbitmap); } else { bit.LoadBitmap(IDB_STail);//尾巴 if (m_changeTail) { switch (m_iDircFlag) { case 0: m_iTailDegree=90; break; case 1: m_iTailDegree=-90; break; case 2: m_iTailDegree=180; break; case 3: m_iTailDegree=0; break; } m_changeTail=FALSE; } hbitmap=(HBITMAP)GetRotatedBitmapNT(bit,m_iTailDegree*3.14159265359/180,pDC->GetBkColor());//旋转图片 bit.Detach(); bit.Attach(hbitmap); } } else { bit.LoadBitmap(IDB_SBody); } CWnd *cw=GetDlgItem(IDC_GROUP_MAIN); HDC hdc=cw->GetDC()->GetSafeHdc(); HDC memDc=CreateCompatibleDC(NULL); (HBITMAP)SelectObject(memDc,bit); BITMAP bm; bit.GetBitmap(&bm); CPoint cp=m_snake.m_points[i]; BitBlt(hdc,cp.x,cp.y+8,bm.bmWidth,bm.bmHeight,memDc,0,0,SRCCOPY); DeleteDC(memDc);//必须释放,要不占内存 DeleteDC(hdc);//必须释放,要不占内存 } DrawFoodPoint();//画食物点 } ///////////////////////////////////////////////////////////////////////////// // CSnakeView printing BOOL CSnakeView::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } void CSnakeView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing } void CSnakeView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing } ///////////////////////////////////////////////////////////////////////////// // CSnakeView diagnostics #ifdef _DEBUG void CSnakeView::AssertValid() const { CFormView::AssertValid(); } void CSnakeView::Dump(CDumpContext& dc) const { CFormView::Dump(dc); } CSnakeDoc* CSnakeView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSnakeDoc))); return (CSnakeDoc*)m_pDocument; } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CSnakeView message handlers void CSnakeView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default CFormView::OnKeyDown(nChar, nRepCnt, nFlags); } //蛇移动事件 void CSnakeView::Move() { int size=m_snake.m_points.GetSize(); CArray<CPoint,CPoint&> arr_points;//复制一个临时的数组 arr_points.Copy(m_snake.m_points); //思路:移动时,除了第一个坐标根据上下左右的不同,重新赋值外,其他的坐标复制他们前面一个坐标的值。 CPoint origTail=m_snake.m_points[size-2]; for (int i=1;i<size;i++) { m_snake.m_points[i]=arr_points[i-1]; } //获取group范围 判断蛇的位置是否超出范围,如果是的话,则提示失败,重新开始 CWnd *cGroupWnd=GetDlgItem(IDC_GROUP_MAIN); RECT groupRect; cGroupWnd->GetClientRect(&groupRect); BOOL blIsOut=FALSE; switch (m_iDircFlag) { case 0://上 m_snake.m_points[0].y-=16; if (m_snake.m_points[0].y<groupRect.top)//超出范围 { blIsOut=TRUE; break; } if (origTail.y!=m_snake.m_points[size-2].y)//判断倒数第二个的x,y是否变化,来决定尾巴图片是否旋转 { m_changeTail=TRUE; } break; case 1://下 m_snake.m_points[0].y+=16; if (m_snake.m_points[0].y+16>groupRect.bottom)//超出范围 { blIsOut=TRUE; break; } if (origTail.y!=m_snake.m_points[size-2].y) { m_changeTail=TRUE; } break; case 2://左 m_snake.m_points[0].x-=16; if (m_snake.m_points[0].x<groupRect.left)//超出范围 { blIsOut=TRUE; break; } if (origTail.x!=m_snake.m_points[size-2].x) { m_changeTail=TRUE; } break; case 3://右 m_snake.m_points[0].x+=16; if (m_snake.m_points[0].x+16>groupRect.right)//超出范围 { blIsOut=TRUE; break; } if (origTail.x!=m_snake.m_points[size-2].x) { m_changeTail=TRUE; } break; } if (blIsOut) { m_snake.Initialize();//重新初始化 m_iDircFlag=3;//方向初始化 m_changeTail=FALSE;//尾巴是否改变初始化 m_iTailDegree=0;//尾巴角度初始化 KillTimer(TimerMain); MessageBox("失败!"); } //吃东西,如果头坐标和食物坐标相等,追加一个坐标 if (m_snake.m_points[0].x==m_pFood.x&&m_snake.m_points[0].y+8==m_pFood.y) { m_snake.m_points.Add(arr_points[size-1]); CString cs; cs.Format("%d",m_snake.m_points.GetSize()); SetDlgItemText(IDC_SNAKELEN,cs); GenerateFoodPoint();//生成食物点 } //重绘客户区 RECT temprect(groupRect); temprect.right+=32; temprect.bottom+=32; InvalidateRect(&temprect); } // GetRotatedBitmapNT - Create a new bitmap with rotated image // Returns - Returns new bitmap with rotated image // hBitmap - Bitmap to rotate // radians - Angle of rotation in radians // clrBack - Color of pixels in the resulting bitmap that do // not get covered by source pixels HBITMAP GetRotatedBitmapNT( HBITMAP hBitmap, float radians, COLORREF clrBack ) { // Create a memory DC compatible with the display CDC sourceDC, destDC; sourceDC.CreateCompatibleDC(NULL); destDC.CreateCompatibleDC(NULL); // Get logical coordinates BITMAP bm; GetObject( hBitmap, sizeof( bm ), &bm ); float cosine = (float)cos(radians); float sine = (float)sin(radians); // Compute dimensions of the resulting bitmap // First get the coordinates of the 3 corners other than origin int x1 = (int)(bm.bmHeight * sine); int y1 = (int)(bm.bmHeight * cosine); int x2 = (int)(bm.bmWidth * cosine + bm.bmHeight * sine); int y2 = (int)(bm.bmHeight * cosine - bm.bmWidth * sine); int x3 = (int)(bm.bmWidth * cosine); int y3 = (int)(-bm.bmWidth * sine); int minx = min(0,min(x1, min(x2,x3))); int miny = min(0,min(y1, min(y2,y3))); int maxx = max(0,max(x1, max(x2,x3))); int maxy = max(0,max(y1, max(y2,y3))); int w = maxx - minx; int h = maxy - miny; // Create a bitmap to hold the result HBITMAP hbmResult=::CreateCompatibleBitmap(CClientDC(NULL), w, h); HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap ); HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult ); // Draw the background color before we change mapping mode HBRUSH hbrBack = CreateSolidBrush( clrBack ); HBRUSH hbrOld = (HBRUSH)::SelectObject(destDC.m_hDC, hbrBack ); destDC.PatBlt( 0, 0, w, h, PATCOPY ); ::DeleteObject(::SelectObject(destDC.m_hDC, hbrOld)); // We will use world transform to rotate the bitmap SetGraphicsMode(destDC.m_hDC, GM_ADVANCED); XFORM xform; xform.eM11 = cosine; xform.eM12 = -sine; xform.eM21 = sine; xform.eM22 = cosine; xform.eDx = (float)-minx; xform.eDy = (float)-miny; SetWorldTransform( destDC.m_hDC, &xform ); // Now do the actual rotating - a pixel at a time destDC.BitBlt(0,0,bm.bmWidth, bm.bmHeight, &sourceDC, 0, 0, SRCCOPY ); // Restore DCs SelectObject(sourceDC.m_hDC, hbmOldSource ); SelectObject(destDC.m_hDC, hbmOldDest ); return hbmResult; } void CSnakeView::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default Move();//贪食蛇移动事件 CFormView::OnTimer(nIDEvent); } BOOL CSnakeView::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if (pMsg->message==WM_KEYDOWN) { switch (pMsg->wParam) { case VK_UP: m_iDircFlag=0; break; case VK_DOWN: m_iDircFlag=1; break; case VK_LEFT: m_iDircFlag=2; break; case VK_RIGHT: m_iDircFlag=3; break; } Move(); } return CFormView::PreTranslateMessage(pMsg); } void CSnakeView::OnBtnStart() { // TODO: Add your control notification handler code here if (i_Speed==0) { SetTimer(TimerMain,500,NULL); } else if (i_Speed==1) { SetTimer(TimerMain,300,NULL); } else { SetTimer(TimerMain,100,NULL); } } //画食物的点 void CSnakeView::DrawFoodPoint() { CWnd *cwnd=GetDlgItem(IDC_GROUP_MAIN); CBitmap bit; bit.LoadBitmap(IDB_SBody); BITMAP bm; bit.GetBitmap(&bm); HDC hdc=CreateCompatibleDC(NULL); (HBITMAP)SelectObject(hdc,bit); HDC grouphdc=cwnd->GetDC()->GetSafeHdc(); BitBlt(grouphdc,m_pFood.x,m_pFood.y,bm.bmWidth,bm.bmHeight,hdc,0,0,SRCCOPY); DeleteDC(grouphdc); DeleteDC(hdc); } //重新生成食物点 void CSnakeView::GenerateFoodPoint() { if (iRandomSeed==0) { srand((unsigned)time(0));//用当前时间 } else { srand(iRandomSeed); } iRandomSeed++; CWnd *cGroupWnd=GetDlgItem(IDC_GROUP_MAIN); RECT groupRect; cGroupWnd->GetClientRect(&groupRect); CBitmap bit; bit.LoadBitmap(IDB_SBody); BITMAP bm; bit.GetBitmap(&bm); while (TRUE) { BOOL blflag=FALSE;//是否需要重新生成food的点 m_pFood.x=(int)(rand()%(groupRect.right/16))*bm.bmWidth; m_pFood.y=(int)(rand()%(groupRect.bottom/16))*bm.bmHeight+8; for (int i=0;i<m_snake.m_points.GetSize(); ++i) { if ((m_snake.m_points[i].x==m_pFood.x)&&(m_snake.m_points[i].y==m_pFood.y)) { blflag=TRUE; break; } } if (!blflag) { break; } } } void CSnakeView::OnShowWindow(BOOL bShow, UINT nStatus) { CFormView::OnShowWindow(bShow, nStatus); GenerateFoodPoint();//生成点 // TODO: Add your message handler code here } void CSnakeView::OnGameSet() { // TODO: Add your command handler code here GAMESET gms; if (IDOK==gms.DoModal()) { i_Speed=gms.m_iSpeed; } }