c++ - C++ 输出参数导致内存泄漏
问题描述
我正在开发一个基于 COM 的 C++ 项目,我的代码有一个带有 out 参数的函数,该函数将一个对象作为输入并将一个类的新实例分配给它。但是当我使用 CRT 调试时,我发现函数中有一些内存泄漏,这是函数的代码。
bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
if (compositionHost != nullptr)
{
*compositionHost = reinterpret_cast<ICompositionHost*>(new CompositionHost(hwnd));
if (compositionHost != nullptr)
{
return true;
}
}
else
{
return false;
}
return false;
}
这个函数接受一个对象ICompositionHost
并用对象初始化它,CompositionHost
我应该在哪里释放内存以避免内存泄漏。
我使用 ComPtr 调用函数,但仍然存在内存泄漏
ComPtr<ICompositionHost> host;
compositor->CreateCompositionHost(hwnd,host.GetAddressOf());
完整代码:
FluentCompositor.cpp
#include "pch.h"
#include "ICompositionHost.h"
#include "IFluentCompositor.h"
#include "CompositionHost.h"
#include "FluentCompositor.h"
FluentCompositor::FluentCompositor() :ref(1)
{
}
ulong __stdcall FluentCompositor::AddRef()
{
return (++ref);
}
ulong __stdcall FluentCompositor::Release()
{
if (--ref == 0)
{
delete this;
return 0;
}
return ref;
}
HRESULT __stdcall FluentCompositor::QueryInterface(REFIID iid, LPVOID* ppv)
{
if (iid == IID_IFluentCompositor || iid == IID_IUnknown)
{
*ppv = (void*)this;
AddRef();
}
else
{
*ppv = NULL;
}
return (*ppv == NULL) ? E_NOINTERFACE : S_OK;
}
HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
if (compositor != nullptr)
{
*compositor = reinterpret_cast<void*>(new FluentCompositor());
if (compositor != nullptr)
{
return S_OK;
}
}
else
{
return E_INVALIDARG;
}
return E_FAIL;
}
bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
if (compositionHost != nullptr)
{
*compositionHost = reinterpret_cast<ICompositionHost*>(new CompositionHost(hwnd));
if (compositionHost != nullptr)
{
return true;
}
}
else
{
return false;
}
return false;
}
主窗口.cpp
#include "pch.h"
#include "MainWindow.h"
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
const wchar_t className[] = L"Fluent Compositor";
WNDCLASS wc = {
.lpfnWndProc = WndProc,
.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase),
.hCursor = LoadCursor(nullptr, IDC_ARROW),
.lpszClassName = className,
};
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP, className, L"Fluent Compositor Sample", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, wc.hInstance, nullptr);
if (hwnd == nullptr)
{
return 0;
}
CreateCompositionEffect(hwnd);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
_CrtDumpMemoryLeaks();
return 0;
}
bool CreateCompositionEffect(HWND hwnd)
{
auto fluentCompositorLib = LoadLibrary(L"FluentCompositor.dll");
if (!fluentCompositorLib)
{
return false;
}
CreateCompositor = (CreateFluentCompositor)GetProcAddress(fluentCompositorLib, "CreateFluentCompositor");
if (!CreateCompositor)
{
return false;
}
CreateCompositor(&compositor);
if (compositor == nullptr)
{
return false;
}
ComPtr<ICompositionHost> host;
compositor->CreateCompositionHost(hwnd,&host);
return true;
}
LRESULT __stdcall WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
主窗口.h
#pragma once
#include "IFluentCompositor.h"
using namespace Microsoft::WRL;
extern "C" IMAGE_DOS_HEADER __ImageBase;
ComPtr<IFluentCompositor> compositor;
typedef BOOL(__stdcall* CreateFluentCompositor)(IFluentCompositor** compositor);
CreateFluentCompositor CreateCompositor;
bool CreateCompositionEffect(HWND hwnd);
LRESULT __stdcall WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
解决方案
您对reinterpret_cast
(以及一般的类型转换)的使用是完全错误的。如果您的类实现了正确的接口,则无需手动转换它们(可能 in 除外QueryInterface()
),编译器将为您隐式执行正确的转换。
此外,您没有new
正确检查 的返回值。或者,就此而言,处理new
默认情况下在失败时抛出异常的可能性,而不是返回nullptr
。如果您想要nullptr
失败,请改用nothrow
of 的版本new
。
另外,在使用时ComPtr
,应该使用它的重载operator&
而不是它的GetAddressOf()
方法。特别是如果ComPtr
已经拥有一个接口。GetAddressOf()
不会释放接口(这就是为什么有一个单独的ReleaseAndGetAddressOf()
方法),但operator&
会。
试试这个:
#include <new>
HRESULT __stdcall FluentCompositor::QueryInterface(REFIID iid, LPVOID* ppv)
{
if (!ppv) return E_POINTER;
if (iid == IID_IFluentCompositor)
{
*ppv = static_cast<IFluentCompositor*>(this);
/* alternatively:
IFluentCompositor *comp = this;
*ppv = comp;
*/
}
else if (iid == IID_IUnknown)
{
*ppv = static_cast<IUnknown*>(static_cast<IFluentCompositor*>(this));
/* alternatively:
IFluentCompositor *comp = this;
IUnknown *unk = comp;
*ppv = unk;
*/
}
else
{
*ppv = nullptr;
}
if (!*ppv)
return E_NOINTERFACE;
AddRef();
return S_OK;
}
// similar for CompositionHost::QueryInterface() ...
HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
if (!compositor) return E_POINTER; // not E_INVALIDARG
// make sure FluentCompositor has a refcount of 1 when created!
*compositor = static_cast<IFluentCompositor*>(new(std::nothrow) FluentCompositor);
return (*compositor) ? S_OK : E_FAIL;
}
bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
if (!compositionHost) return false;
// make sure CompositionHost has a refcount of 1 when created!
*compositionHost = new(std::nothrow) CompositionHost(hwnd);
return (*compositionHost);
}
ComPtr<ICompositionHost> host;
compositor->CreateCompositionHost(hwnd, &host);
或者,ComPtr
在创建对象时考虑在内部使用,例如:
#include <new>
HRESULT __stdcall CreateFluentCompositor(void** compositor)
{
if (!compositor) return E_POINTER; // not E_INVALIDARG
// make sure FluentCompositor has a refcount of 0 when created,
// as the ComPtr constructor will increment it!
ComPtr<IFluentCompositor> obj(new(std::nothrow) FluentCompositor);
return (obj) ? obj->QueryInterface(IID_IFluentCompositor, compositor) : E_FAIL;
}
bool __stdcall FluentCompositor::CreateCompositionHost(HWND hwnd, ICompositionHost** compositionHost)
{
// make sure CompositionHost has a refcount of 0 when created,
// as the ComPtr constructor will increment it!
ComPtr<ICompositionHost> obj(new(std::nothrow) CompositionHost(hwnd));
return ((obj) && (obj->QueryInterface(IID_ICompositionHost, reinterpret_cast<void**>(compositionHost)) == S_OK));
}
让您的对象以 0 而不是 1 的引用计数开始是个好主意,因为它们不知道它们是否将与接口指针或对象指针一起使用(如果它们甚至与指针一起使用) . 不要增加一个对象的引用计数,除非它实际上被分配给一个被AddRef()
'ed 并且需要被Release()
'd 的接口指针。
推荐阅读
- asp.net-core - 如何在 ASP.Net Core 应用程序中使用 Serilog 在单独的列中记录异常类型?
- arrays - 检查数组是否包含值时出现 Fortran“ANY”函数错误
- asp.net-core - Asp Net Core 电子邮件验证
- jquery - 为什么我的 jquery 只能按顺序工作?
- r - gganimate::transition_time 导致飞行多边形
- reactjs - 如何在 React Native 的倒计时中制作幻灯片动画?
- oracle - 如何通过连接两个在 oracle sql 中没有公共列的表来创建过程?
- python - vncdotool 无法连接到远程 vnc 服务器 python
- c# - C# 中 ASP MVC 项目中没有参数的路由预期路由参数“id”
- javascript - 反应一个对象的更新状态改变另一个对象