C++输出参数导致内存泄漏
C++ Output Parameter Causes Memory Leak
我正在开发一个基于 COM 的 C++ 项目,我的代码有一个带有 out 参数的函数,该函数将一个对象作为输入并将 class 的新实例分配给它。但是当我使用CRT Debugging时,我发现函数中存在内存泄漏,这是函数的代码。
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;
}
MainWindow.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);
}
MainWindow.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
的使用(以及一般的类型转换)完全是错误的。如果您的 类 实现了正确的接口,则无需手动转换它们(可能在 QueryInterface()
中除外),编译器将隐式执行 correct 转换你.
此外,您没有正确检查 new
的 return 值。或者,就此而言,处理 new
默认情况下在失败时抛出异常的可能性,而不是 returns nullptr
。如果你想在失败时使用 nullptr
,请改用 new
的 nothrow
版本。
此外,在使用 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()
并且需要 Release()
。
我正在开发一个基于 COM 的 C++ 项目,我的代码有一个带有 out 参数的函数,该函数将一个对象作为输入并将 class 的新实例分配给它。但是当我使用CRT Debugging时,我发现函数中存在内存泄漏,这是函数的代码。
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;
}
MainWindow.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);
}
MainWindow.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
的使用(以及一般的类型转换)完全是错误的。如果您的 类 实现了正确的接口,则无需手动转换它们(可能在 QueryInterface()
中除外),编译器将隐式执行 correct 转换你.
此外,您没有正确检查 new
的 return 值。或者,就此而言,处理 new
默认情况下在失败时抛出异常的可能性,而不是 returns nullptr
。如果你想在失败时使用 nullptr
,请改用 new
的 nothrow
版本。
此外,在使用 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()
并且需要 Release()
。