c++ - 如何释放在子对话框中创建的 CWin 对象以避免内存泄漏

标签 c++ visual-c++ mfc

我正在子对话框中创建一个 CStatic 控件,它工作正常。问题是关闭子对话框后内存没有正确释放。

我试图像这个线程中描述的那样覆盖 PostNCDestoy: Is this a memory leak in MFC 但这通过调用“删除这个”给了我一个未处理的异常。

知道释放 CStatic 和 CButton 以避免内存泄漏的正确方法是什么吗?

CChildDlg.h

class CChildDlg
{
  std::vector<CStatic*> m_images;
  void addNewImage(const int &xPos, const int &yPos)
  ...
}

CChildDlg.cpp

void CChildDlg::addNewImage(const int &xPos, const int &yPos){

  CImage imgFromFile;
  CStatic *img = new CStatic;

  imgFromFile.Load(_T("someImg.jpg"));
  int width = imgFromFile.GetWidth();
  int height = imgFromFile.GetHeight();


  img->Create(_T("Image"), WS_CHILD | WS_VISIBLE | SS_BITMAP,
    CRect(xPos, yPos, xPos + width, yPos + height), this, 10910);

  HBITMAP hbmOld = img->SetBitmap(imgFromFile.Detach());
  if (hbmOld != nullptr){
    ::DeleteObject(hbmOld);
  }
  m_images.pushback(img);
}

根据该线程中的建议,我更改了如下代码:

CChildDlg.h

class CChildDlg
{
private:
  typedef std::vector<std::unique_ptr <CStatic>> CStaticImgs;
  CStaticImgs m_images;
  void addNewImage(const int &xPos, const int &yPos)
  ...
}

CChildDlg.cpp

void CChildDlg::addNewImage(const int &xPos, const int &yPos){

  CImage imgFromFile;
  std::unique_ptr<CStatic> img(new CStatic);

  imgFromFile.Load(_T("someImg.jpg"));
  int width = imgFromFile.GetWidth();
  int height = imgFromFile.GetHeight();

  img->Create(_T("Image"), WS_CHILD | WS_VISIBLE | SS_BITMAP,
     CRect(xPos, yPos, xPos + width, yPos + height), this, 10910);

  HBITMAP hbmOld = img->SetBitmap(imgFromFile.Detach());
  if (hbmOld != nullptr){
     ::DeleteObject(hbmOld);
  }
  m_images.pushback(std::move(img));
}

代码工作正常,但漏洞仍然存在。只有当我删除将位图设置为 CStatic 的行时,泄漏才会消失:

//HBITMAP hbmOld = img->SetBitmap(imgFromFile.Detach());
//if (hbmOld != nullptr){
  //::DeleteObject(hbmOld);
//}

因此,它必须与以某种方式将 CImage 的所有权移交给 CStatic 有关。我正在向对话框加载多达 100 张图像。每次打开对话框时,我仍然可以看到内存显着增加,关闭后内存不会下降。

任何其他建议可能会丢失什么错误?

最佳答案

天真的解决方案是简单地遍历您的容器类,调用 delete在每个指针上。像这样的东西:

for (auto i : m_images)                       { delete i; }            // on C++11
for (size_t i = 0; i < m_images.size(); ++i)  { delete m_images[i]; }  // on C++03

如果您在析构函数中执行此操作或响应 WM_DESTROY消息(MFC 中的 OnDestroy),它将确保您的每个 CStatic实例被销毁,解决了内存泄漏问题。

但这不是最好的解决方案。在 C++ 中,您应该利用 Scope-Bound Resource Management (SBRM) ,通常也称为 RAII。这涉及到使用语言特性来自动清理对象,而不必手动进行。这不仅使代码更清晰,而且确保您永远不会忘记,您的代码是完全异常安全的,甚至可能更高效。

通常,您只需将对象本身存储在容器类中。也就是说,而不是 std::vector<CStatic*> , 你只会有 std::vector<CStatic> .这样,每当 vector 容器被销毁(当它超出范围时自动发生,感谢 SBRM),它包含的所有对象也会被销毁(,它们的析构函数被调用自动)。

但是,标准库容器要求对象是可复制的或可移动的。从 CObject 派生的 MFC 类不可复制,并且大概也不可移动(鉴于 MFC 的时代和使对象不可复制的标准习语隐式使其不可移动)。这意味着这行不通:您无法存储 CStatic vector 或其他容器类中的对象本身。

幸运的是,现代 C++ 有解决这个问题的方法:smart pointer .智能指针就像它们听起来的那样:一个包装裸(“哑”)指针的类,赋予它超能力。智能指针提供的智能中最主要的是 SBRM。每当智能指针对象被销毁时,它会自动删除其底层哑指针。这让你,谦逊的程序员,不必再写一个 delete 了。陈述。事实上,在 C++ 中您几乎不必做的两件事是:

  • 明确写delete
  • 使用原始指针

鉴于 MFC 的限制,我的建议是 CObject -derived classes,就是在你的vector中存储智能指针。语法不是很漂亮,但可以通过 typedef 轻松解决:

typedef std::vector<std::unique_ptr<CStatic>> CStaticVector;   // for C++11

(对于 C++03,由于 std::auto_ptr cannot be used in a standard container [同样,因为它不可复制],您需要使用不同的智能指针,例如 Boost Smart Pointers library。)

不再有手动内存管理,不再有内存泄漏,不再有麻烦。在 C++ 中工作从字面上的痛苦变成了快速、有趣和高效。

关于c++ - 如何释放在子对话框中创建的 CWin 对象以避免内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38118923/

相关文章:

c++ - 在不相关的类中使用对象

c++ - 如何从键盘读取 C++ 中可变数量的整数?

c++ - 在 MSVC 和 g++ 中使用模板的差异

java - c++ static 关键字对大括号有什么作用?

c++ - CMFCStatusBar 双击事件

mfc - 升级后的MFC应用程序看起来仍然很旧

c++ - 错误 C3861 : DrawTarget: dentifier not found

c++ - 如何处理异步函数中的异常 UWP App GetFileFromPathAsync(path);

c++ - 在 2 个 cpp 文件中定义一个 C++ 类?

c++ - SetWindowPos 在笔记本电脑上使用放大的对话框