DirectX11--ComPtr智能指針


綜述

DirectX11 With Windows SDK完整目錄

歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什么問題也可以在這里匯報。

IUnknown接口類

DirectX11的API是由一系列的COM組件來管理的,這些前綴帶I的接口類最終都繼承自IUnknown接口類。IUnknown的三個方法如下:

方法 描述
IUnknown::AddRef 內部引用計數加1。在每次復制了一個這樣的指針后,應當調用該方法以保證計數准確性
IUnknown::QueryInterface 查詢該實例是否實現了另一個接口,如果存在則返回該接口的指針,並且對該接口的引用計數加1
IUnknown::Release 內部引用數減1。只有當內部引用數到達0時才會真正釋放

在實際的使用情況來看,通常我們幾乎不會使用第一個方法。而用的最多的就是第三個方法了,每次用完該實例后,我們必須要使用類似下面的宏來釋放:

#define ReleaseCOM(x) { if(x){ x->Release(); x = nullptr; } }

而且如果出現了忘記釋放某個接口指針的情況話,內存泄漏的提醒就有可能夠你去調試一整天了。

ComPtr智能指針

為了解決上述問題,從繁雜的人工釋放中解脫,在本教程中大量使用了ComPtr智能指針。而且在龍書12的教程源碼中也用到了該智能指針。該智能指針可以幫助我們來管理這些COM組件實現的接口實例,而無需過多擔心內存的泄漏。該智能指針的大小和一般的指針大小是一致的,沒有額外的內存空間占用。所以本教程可以不需要用到接口類ID3D11Debug來協助檢查內存泄漏。

使用該智能指針需要包含頭文件wrl/client.h,並且智能指針類模板ComPtr位於名稱空間Microsoft::WRL內。

首先有五個比較常用的方法需要了解一下:

方法 描述
ComPtr ::Get 該方法返回T*,並且不會觸發引用計數加1,常用在COM組件接口的函數輸入
ComPtr ::GetAddressOf 該方法返回T**,常用在COM組件接口的函數輸出
ComPtr ::Reset 該方法對里面的實例調用Release方法,並將指針置為nullptr
ComPtr ::ReleaseAndGetAddressOf 該方法相當於先調用Reset方法,再調用GetAddressOf方法獲取T**,常用在COM組件接口的函數輸出,適用於實例可能會被反復構造的情況下
ComPtr ::As 一個模板函數,可以替代IUnknown::QueryInterface的調用,需要傳遞一個ComPtr實例的地址

然后是一些運算符重載的方法:

運算符 描述
& 相當於調用了ComPtr<T>::ReleaseAndGetAddressOf方法,不推薦使用
-> 和裸指針的行為一致
= 不要將裸指針指向的實例賦給它,若傳遞的是ComPtr 的不同實例則發生交換
==和!= 可以和nullptr,或者另一個ComPtr 實例進行比較

注意:大致在比10.0.16299.0更早的Windows SDK版本中,ComPtr使用了一個RemoveIUnknownBase類模板將IUnknown的三個接口都設為了private,以防止用戶直接操作這些方法,這也就使得ComPtr無法直接使用COM組件的QueryInterface方法。因此,使用ComPtr<T>::As是一種合適的選擇。

個人建議,在使用該智能指針后就應該要避免使用IUnknown提供的三個接口方法來進行操作。

雖然替換成ComPtr后代碼量變長了,但是帶來的收益肯定比你自己花費大量時間在檢查釋放內存上強的多。

下面的D3DApp將所有COM組件指針都換成了ComPtr

class D3DApp
{
public:
	D3DApp(HINSTANCE hInstance);              // 在構造函數的初始化列表應當設置好初始參數
	virtual ~D3DApp();

	HINSTANCE AppInst()const;                 // 獲取應用實例的句柄
	HWND      MainWnd()const;                 // 獲取主窗口句柄
	float     AspectRatio()const;             // 獲取屏幕寬高比

	int Run();                                // 運行程序,進行游戲主循環

	                                          // 框架方法。客戶派生類需要重載這些方法以實現特定的應用需求
	virtual bool Init();                      // 該父類方法需要初始化窗口和Direct3D部分
	virtual void OnResize();                  // 該父類方法需要在窗口大小變動的時候調用
	virtual void UpdateScene(float dt) = 0;   // 子類需要實現該方法,完成每一幀的更新
	virtual void DrawScene() = 0;             // 子類需要實現該方法,完成每一幀的繪制
	virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
	// 窗口的消息回調函數
protected:
	bool InitMainWindow();      // 窗口初始化
	bool InitDirect3D();        // Direct3D初始化

	void CalculateFrameStats(); // 計算每秒幀數並在窗口顯示

protected:

	HINSTANCE m_hAppInst;        // 應用實例句柄
	HWND      m_hMainWnd;        // 主窗口句柄
	bool      m_AppPaused;       // 應用是否暫停
	bool      m_Minimized;       // 應用是否最小化
	bool      m_Maximized;       // 應用是否最大化
	bool      m_Resizing;        // 窗口大小是否變化
	bool	  m_Enable4xMsaa;	 // 是否開啟4倍多重采樣
	UINT      m_4xMsaaQuality;   // MSAA支持的質量等級


	GameTimer m_Timer;           // 計時器

	// 使用模板別名(C++11)簡化類型名
	template <class T>
	using ComPtr = Microsoft::WRL::ComPtr<T>;
	// Direct3D 11
	ComPtr<ID3D11Device> m_pd3dDevice;                    // D3D11設備
	ComPtr<ID3D11DeviceContext> m_pd3dImmediateContext;   // D3D11設備上下文
	ComPtr<IDXGISwapChain> m_pSwapChain;                  // D3D11交換鏈
	// Direct3D 11.1
	ComPtr<ID3D11Device1> m_pd3dDevice1;                  // D3D11.1設備
	ComPtr<ID3D11DeviceContext1> m_pd3dImmediateContext1; // D3D11.1設備上下文
	ComPtr<IDXGISwapChain1> m_pSwapChain1;                // D3D11.1交換鏈
	// 常用資源
	ComPtr<ID3D11Texture2D> m_pDepthStencilBuffer;        // 深度模板緩沖區
	ComPtr<ID3D11RenderTargetView> m_pRenderTargetView;   // 渲染目標視圖
	ComPtr<ID3D11DepthStencilView> m_pDepthStencilView;   // 深度模板視圖
	D3D11_VIEWPORT m_ScreenViewport;                      // 視口

	// 派生類應該在構造函數設置好這些自定義的初始參數
	std::wstring m_MainWndCaption;                       // 主窗口標題
	int m_ClientWidth;                                   // 視口寬度
	int m_ClientHeight;                                  // 視口高度
};

DirectX11 With Windows SDK完整目錄

歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什么問題也可以在這里匯報。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM