我正在子对话框中创建一个 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/