我正在试验单例模式,以便更轻松地使用 DirectX API 的一些常见接口(interface),但我有一个似乎无法解决的问题。
这是我的定义:
#include <d3d11.h>
#include <d3dx11.h>
#include <DxErr.h>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dx11.lib")
#pragma comment(lib, "DxErr.lib")
class Core
{
public:
~Core();
static ID3D11Device*& getDevice();
static ID3D11DeviceContext*& getContext();
static IDXGISwapChain*& getSwapChain();
static ID3D11RenderTargetView*& getRenderTargetView();
static ID3D11Debug*& getDebug();
static HRESULT Initialize(HWND hwnd);
static void Reset();
private:
Core();
HRESULT CreateDevice();
static Core* instance;
ID3D11Device* device;
ID3D11DeviceContext* context;
IDXGISwapChain* swapChain;
IDXGIDevice1* ddevice;
IDXGIAdapter1* adapter;
IDXGIFactory1* factory;
ID3D11RenderTargetView* rtv;
ID3D11Debug* debug;
Core(Core const&);
void operator=(Core const&);
};
这是实现:
#include "Core.h"
Core* Core::instance;
Core::Core()
{
HRESULT hr;
hr = CreateDevice();
if(FAILED(hr)) throw hr;
}
Core::~Core()
{
if (device) device->Release();
if (context) context->Release();
if (swapChain) swapChain->Release();
if (ddevice) ddevice->Release();
if (adapter) adapter->Release();
if (factory) factory->Release();
if (rtv) rtv->Release();
debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
}
//--------------------------------------------------------------------------------
// Get Functions
//--------------------------------------------------------------------------------
ID3D11Device*& Core::getDevice()
{
if (!instance)
{
try
{
instance = new Core();
}
catch (HRESULT hr)
{
throw hr;
}
}
return instance->device;
}
ID3D11DeviceContext*& Core::getContext()
{
if (!instance)
{
try
{
instance = new Core();
}
catch (HRESULT hr)
{
throw hr;
}
}
return instance->context;
}
IDXGISwapChain*& Core::getSwapChain()
{
if (!instance)
{
try
{
instance = new Core();
}
catch (HRESULT hr)
{
throw hr;
}
}
return instance->swapChain;
}
ID3D11RenderTargetView*& Core::getRenderTargetView()
{
if (!instance)
{
try
{
instance = new Core();
}
catch (HRESULT hr)
{
throw hr;
}
}
return instance->rtv;
}
ID3D11Debug*& Core::getDebug()
{
if (!instance)
{
try
{
instance = new Core();
}
catch (HRESULT hr)
{
throw hr;
}
}
return instance->debug;
}
//--------------------------------------------------------------------------------
// Other Functions
//--------------------------------------------------------------------------------
HRESULT Core::CreateDevice()
{
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
D3D_FEATURE_LEVEL featureLevel;
UINT flags = NULL;
#ifdef _DEBUG
flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
HRESULT hr;
hr = D3D11CreateDevice(
NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
flags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&device,
&featureLevel,
&context);
if(FAILED(hr)) return hr;
hr = device->QueryInterface(__uuidof(ID3D11Debug), (void**) &debug);
if(FAILED(hr)) return hr;
hr = device->QueryInterface(__uuidof(IDXGIDevice1), (void**) &ddevice);
if(FAILED(hr)) return hr;
hr = ddevice->GetParent(__uuidof(IDXGIAdapter1), (void**) &adapter);
if(FAILED(hr)) return hr;
hr = adapter->GetParent(__uuidof(IDXGIFactory1), (void**) &factory);
if(FAILED(hr)) return hr;
return S_OK;
}
HRESULT Core::Initialize(HWND hwnd)
{
// Create the swap chain
RECT rc;
GetClientRect(hwnd, &rc);
UINT width = rc.right - rc.left;
UINT height = rc.bottom - rc.top;
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) );
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hwnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
if (!instance)
{
try
{
instance = new Core();
}
catch (HRESULT hr)
{
return hr;
}
}
instance->factory->CreateSwapChain(instance->device, &sd, &instance->swapChain);
// Create and set the render target view
HRESULT hr;
ID3D11Texture2D* renderingBuffer;
hr = instance->swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &renderingBuffer);
if(FAILED(hr))
{
renderingBuffer->Release();
return hr;
}
hr = instance->device->CreateRenderTargetView(renderingBuffer, NULL, &instance->rtv);
renderingBuffer->Release();
if(FAILED(hr)) return hr;
// TODO: create and set the depth stencil view
instance->context->OMSetRenderTargets(1, &instance->rtv, nullptr);
return S_OK;
}
void Core::Reset()
{
delete instance;
}
析构函数被调用,但一旦我退出应用程序,我仍然在输出窗口中看到这个:
D3D11 WARNING: Live ID3D11Device at 0x007A201C, Refcount: 2 [ STATE_CREATION WARNING #441: LIVE_DEVICE]
D3D11 WARNING: Live ID3D11Context at 0x007A3620, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #2097226: LIVE_CONTEXT]
D3D11 WARNING: Live ID3DDeviceContextState at 0x007ADBB0, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #3145742: LIVE_DEVICECONTEXTSTATE]
D3D11 WARNING: Live ID3D11BlendState at 0x007B3D84, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #435: LIVE_BLENDSTATE]
D3D11 WARNING: Live ID3D11DepthStencilState at 0x007B3ECC, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #436: LIVE_DEPTHSTENCILSTATE]
D3D11 WARNING: Live ID3D11RasterizerState at 0x007B403C, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #437: LIVE_RASTERIZERSTATE]
D3D11 WARNING: Live ID3D11Sampler at 0x007B428C, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #434: LIVE_SAMPLER]
D3D11 WARNING: Live ID3D11Query at 0x007B440C, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #438: LIVE_QUERY]
D3D11 WARNING: Live IDXGISwapChain at 0x007B45B8, Refcount: 0 [ STATE_CREATION WARNING #442: LIVE_SWAPCHAIN]
D3D11 WARNING: Live ID3D11Texture2D at 0x007B4B1C, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #425: LIVE_TEXTURE2D]
D3D11 WARNING: Live ID3D11RenderTargetView at 0x007B4EBC, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #428: LIVE_RENDERTARGETVIEW]
D3D11 WARNING: Live ID3D11Texture2D at 0x007B55EC, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #425: LIVE_TEXTURE2D]
我还检查了析构函数中的 Release() 函数是否被实际调用,它们确实被调用了。
我一定遗漏了一些非常明显的东西,如果有人能向我指出,我将不胜感激。
另外,在这种情况下,这是我使用单例模式的明智之举吗?
谢谢。
编辑:
这是我的 DirectX 类,我在其中使用了 Core 类:
#include "DirectX.h"
DirectX::DirectX() {}
DirectX::DirectX(HWND hwnd)
{
HRESULT hr;
hr = Init(hwnd);
if(FAILED(hr)) throw hr;
hr = Load();
if(FAILED(hr)) throw hr;
}
DirectX::~DirectX()
{
Core::Reset();
}
HRESULT DirectX::Init(HWND hwnd)
{
HRESULT hr;
hr = Core::Initialize(hwnd);
if(FAILED(hr)) return hr;
return S_OK;
}
HRESULT DirectX::Load()
{
return S_OK;
}
HRESULT DirectX::Update()
{
return S_OK;
}
HRESULT DirectX::Render()
{
try
{
float backgroundcolor[4] = {1.0f, 1.0f, 0.0f, 1.0f};
Core::getContext()->ClearRenderTargetView(Core::getRenderTargetView(), backgroundcolor);
Core::getSwapChain()->Present(0, 0);
}
catch (HRESULT hr)
{
return hr;
}
return S_OK;
}
最佳答案
我很高兴你自己已经找到了答案,但我可以给你一些关于单例模式和 Cpp 的建议吗?
对我来说,您似乎是从 C 背景切换到 Cpp。我一直在为执行该切换的团队审查大量代码,这对我来说很熟悉。
你所做的没有单例模式(即使它有那个实例指针)。你有一个指向实例的静态指针并不意味着所有的方法都必须是静态的。 - 您所做的是一个静态函数库,它对结构中的数据进行操作,该结构意外地是它自己的实例。你本可以离开这个类,把数据放在一个结构上,你会得到和你几乎一样的东西。 - 但单例也是另外一种情况,它是一个使用自己的方法运行的对象,数据就像您期望从对象中获得的一样(例如唯一的,不可复制的,房子里的咖啡机),而不是对静态数据进行操作。
Joachim Pileborg 关于指针的句柄也是正确的,这是危险的,因为任何人都可以在每次事故中分配另一个对象,留下内存泄漏和其他影响,这将导致在更大的项目中很难找到错误。你应该只在一个非常好的理由下这样做。
接下来,您应该在构造函数上为所有成员变量明确使用初始化列表,或者在构造函数中以另一种方式初始化它们,否则如果有人获得一个实例并立即在您的类上调用 Reset,则指针都位于随机地址(不是 0 )。意味着析构函数肯定会崩溃。
作为最后一个提示,不要像在 if 语句中使用 bool 值那样使用指针。你写代码,你是作者,试着清楚地传达你在做什么 if(obj == NULL)。
一个基本的单例模式是这样应用的(还有其他更优雅的实例化或线程保存的方法,但这是基本模式):
(请注意,我没有编译它,因为我正在使用 linux 机器 atm,如果在 CreateDevice() 函数中,如果 this->x 在这里和那里有一个 & 在前面可能会出现问题,我没有检查这些)
祝你的项目好运!
核心文件
#include <d3d11.h>
#include <d3dx11.h>
#include <DxErr.h>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dx11.lib")
#pragma comment(lib, "DxErr.lib")
class Core
{
public:
Core* instance();
static void Reset();
~Core();
HRESULT Initialize(HWND hwnd);
ID3D11Device* getDevice();
ID3D11DeviceContext* getContext();
IDXGISwapChain* getSwapChain();
ID3D11RenderTargetView* getRenderTargetView();
ID3D11Debug* getDebug();
private:
static Core* instance;
ID3D11Device* device;
ID3D11DeviceContext* context;
IDXGISwapChain* swapChain;
IDXGIDevice1* ddevice;
IDXGIAdapter1* adapter;
IDXGIFactory1* factory;
ID3D11RenderTargetView* rtv;
ID3D11Debug* debug;
HRESULT CreateDevice();
Core();
Core(Core const&);
void operator=(Core const&);
};
核心文件
#include "Core.h"
Core* Core::instance;
Core* Core::instance()
{
if (instance == NULL)
{
try
{
instance = new Core();
}
catch (HRESULT hr)
{
throw hr;
}
}
}
Core::Core() :
device(NULL),
context(NULL),
swapChain(NULL),
ddevice(NULL),
adapter(NULL),
factory(NULL),
rtv(NULL),
debug(NULL)
{
HRESULT hr;
hr = CreateDevice();
if(FAILED(hr)) throw hr;
}
Core::~Core()
{
if (device != NULL) device->Release();
if (context != NULL) context->Release();
if (swapChain != NULL) swapChain->Release();
if (ddevice != NULL) ddevice->Release();
if (adapter != NULL) adapter->Release();
if (factory != NULL) factory->Release();
if (rtv != NULL) rtv->Release();
debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
}
ID3D11Device* Core::getDevice()
{
return this->device;
}
ID3D11DeviceContext* Core::getContext()
{
return this->context;
}
IDXGISwapChain* Core::getSwapChain()
{
return this->swapChain;
}
ID3D11RenderTargetView* Core::getRenderTargetView()
{
return this->rtv;
}
ID3D11Debug* Core::getDebug()
{
return this->debug;
}
HRESULT Core::CreateDevice()
{
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
D3D_FEATURE_LEVEL featureLevel;
UINT flags = NULL;
#ifdef _DEBUG
flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
HRESULT hr;
hr = D3D11CreateDevice(
NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
flags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&device,
&featureLevel,
&context);
if(FAILED(hr)) return hr;
hr = device->QueryInterface(__uuidof(ID3D11Debug), (void**) &debug);
if(FAILED(hr)) return hr;
hr = device->QueryInterface(__uuidof(IDXGIDevice1), (void**) &ddevice);
if(FAILED(hr)) return hr;
hr = ddevice->GetParent(__uuidof(IDXGIAdapter1), (void**) &adapter);
if(FAILED(hr)) return hr;
hr = adapter->GetParent(__uuidof(IDXGIFactory1), (void**) &factory);
if(FAILED(hr)) return hr;
return S_OK;
}
HRESULT Core::Initialize(HWND hwnd)
{
// Create the swap chain
RECT rc;
GetClientRect(hwnd, &rc);
UINT width = rc.right - rc.left;
UINT height = rc.bottom - rc.top;
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) );
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hwnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
this->factory->CreateSwapChain(this->device, &sd, this->swapChain);
// Create and set the render target view
HRESULT hr;
ID3D11Texture2D* renderingBuffer;
hr = this->swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &renderingBuffer);
if(FAILED(hr))
{
renderingBuffer->Release();
return hr;
}
hr = this->device->CreateRenderTargetView(renderingBuffer, NULL, this->rtv);
renderingBuffer->Release();
if(FAILED(hr)) return hr;
// TODO: create and set the depth stencil view
this->context->OMSetRenderTargets(1, &this->rtv, nullptr);
return S_OK;
}
void Core::Reset()
{
delete instance;
}
用法
#include "DirectX.h"
DirectX::DirectX() {}
DirectX::DirectX(HWND hwnd)
{
HRESULT hr;
hr = Init(hwnd);
if(FAILED(hr)) throw hr;
hr = Load();
if(FAILED(hr)) throw hr;
}
DirectX::~DirectX()
{
Core::instance()->Reset();
}
HRESULT DirectX::Init(HWND hwnd)
{
HRESULT hr;
hr = Core::instance()->Initialize(hwnd);
if(FAILED(hr)) return hr;
return S_OK;
}
HRESULT DirectX::Load()
{
return S_OK;
}
HRESULT DirectX::Update()
{
return S_OK;
}
HRESULT DirectX::Render()
{
try
{
Core* core = Core::instance();
float backgroundcolor[4] = {1.0f, 1.0f, 0.0f, 1.0f};
core->ClearRenderTargetView(core->getRenderTargetView(), backgroundcolor);
core->Present(0, 0);
}
catch (HRESULT hr)
{
return hr;
}
return S_OK;
}
关于c++ - 有人可以解释为什么在释放指向它们的指针后我仍然有事件对象吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20032816/