什么是直接 X 虚拟表?
What is a Direct X Virtual Table?
我有一个关于虚拟表的问题。
我知道的虚拟表是可以找到的函数地址数组 多态对象调用虚函数时的函数地址。
但是在directx中,有人提到了dx vtable,即d3d9的表.dll 的导入函数地址数组。
为什么他们将导入函数地址数组调用到 directx vtable? 它似乎与 vtable 无关。
我的知识有误吗?我可以详细了解 vtable 吗? 谢谢!
从 DLL 导出 COM 对象时,通常有两个部分在工作。首先是"工厂"。工厂可以是从 DLL 导出的标准 C 可调用函数(Direct3D 就是这种情况(,也可以是向系统注册表注册的类,在这种情况下,你将使用CoCreateInstance
来创建 COM 接口实例。第一个示例是创建 Direct3D 设备:
ID3D11Device* d3dDevice = nullptr;
hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
0,
0,
nullptr,
0,
D3D11_SDK_VERSION,
&d3dDevice,
nullptr,
nullptr);
在此调用之后,d3dDevice
接口指向分配的 COM 接口对象。包含D3D11CreateDevice
的 DLL 必须隐式链接到调用程序 - 或者您可以使用LoadLibrary
进行显式链接,在这种情况下,您将使用指向函数的指针D3D11CreateDevice
这相当于大致相同的事情。这是从 DLL 的"导入表"派生的。
第二个示例是使用 Windows 映像组件 (WIC( 时执行的操作:
IWICImagingFactory* factory = nullptr;
hr = CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
&factory);
这让 COM 系统在注册表中查找类 GUID,加载引用的 DLL,然后在其中调用工厂方法来创建一个 COM 接口对象,该对象将返回给你。
在这两种情况下,接口指向的实际内容的形式如下:
typedef struct IUnknownVtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IUnknown * This,
/* [in] */ REFIID riid,
/* [annotation][iid_is][out] */
_COM_Outptr_ void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
IUnknown * This);
ULONG ( STDMETHODCALLTYPE *Release )(
IUnknown * This);
END_INTERFACE
} IUnknownVtbl;
它被设计为精确映射到virtual
的视觉C++实现:
MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")
IUnknown
{
public:
BEGIN_INTERFACE
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [annotation][iid_is][out] */
_COM_Outptr_ void **ppvObject) = 0;
virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
END_INTERFACE
};
COM 实际上没有继承的概念,但同样,ID3D11Device1
使用来自ID3D1Device
的公共继承C++这同样很方便,这仅在语言类型没有声明数据成员时才有效。接口的COM"继承"实际上只是方法的串联,如果您查看标头中的C定义,则可以看到这些方法:
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("cc86fabe-da55-401d-85e7-e3c9de2877e9")
ID3D11BlendState1 : public ID3D11BlendState
{
public:
virtual void STDMETHODCALLTYPE GetDesc1(
/* [annotation] */
_Out_ D3D11_BLEND_DESC1 *pDesc) = 0;
};
#else /* C style interface */
typedef struct ID3D11BlendState1Vtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )( /* ... */ );
ULONG ( STDMETHODCALLTYPE *AddRef )( ID3D11BlendState1 * This);
ULONG ( STDMETHODCALLTYPE *Release )( ID3D11BlendState1 * This);
void ( STDMETHODCALLTYPE *GetDevice )( /* ... */ );
HRESULT ( STDMETHODCALLTYPE *GetPrivateData )( /* ... */ );
HRESULT ( STDMETHODCALLTYPE *SetPrivateData )( /* ... */ );
HRESULT ( STDMETHODCALLTYPE *SetPrivateDataInterface )( /* ... */ );
void ( STDMETHODCALLTYPE *GetDesc )(
ID3D11BlendState1 * This,
/* [annotation] */
_Out_ D3D11_BLEND_DESC *pDesc);
void ( STDMETHODCALLTYPE *GetDesc1 )(
ID3D11BlendState1 * This,
/* [annotation] */
_Out_ D3D11_BLEND_DESC1 *pDesc);
END_INTERFACE
} ID3D11BlendState1Vtbl;
#endif
重要的是要注意,这仅适用于没有数据成员且仅使用公共继承(即接口(的虚拟方法的纯虚拟(即抽象(C++类的情况。构造函数和析构函数将被忽略,因为 COM 生存期是通过
IUnknown
的引用计数来管理的。
方法的调用签名还将指向 COM 对象的指针作为映射到this
的C++调用约定的第一个参数。
因此,COM 接口设计为像C++虚拟方法一样工作,因此您可以使用C++语法来调用它们,但它们不一定C++类对象。
从IUnknown
来看,COM中有一种已知的标准方法来获取特定的接口,即QueryInterface
。Direct3D 工厂函数也会为你处理这个问题,只返回基本接口。在Direct3D 11的情况下,这是一个ID3D11Device
。
如果你想让一个接口说 11.1,那么你在
ID3D11Device
上使用QueryInterface
要求ID3D11Device1
。它在 DirectX 11.0 系统上会失败,但在 DirectX 11.1 或更高版本的系统上工作。
对于使用 Direct3D 的C++程序员,该设计有意将"COM"保持在最低限度(俗称"COM lite"(。实际上,使用足够的COM可以更轻松地处理随时间推移的接口更改并提供合理的ABI。Direct3D 工厂函数是从已知 DLL 进行的简单 C 可调用导出,因此您甚至不必使用标准 COM 工厂,事实上,如果您尝试使用CoCreateInstance
,API 并非设计为工作。从技术上讲,您可以通过标准 MIDL 编译器生成的一些宏以及C++定义来使用 C,但这有点挑战性,而且现在没有经过特别好的测试。
作为 Direct3D COM 的使用者,你需要了解的只是IUnknown
引用计数和查询的基础知识(目前最好使用 Microsoft::WRL::ComPtr 智能指针来完成(,以及如何正确检查HRESULT
值 - 请参阅 ThrowIfFailed。
请参阅组件对象模型和引用计数 (Direct3D 10(
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 让编译器告诉什么确切的纯虚拟方法使结构抽象?
- 出于什么目的,非虚拟方法将与C++一起使用?
- 私有虚拟方法有什么用?
- 拥有"受保护的非虚拟析构函数"与"受保护虚拟析构构函数"有什么好处
- 将虚拟方法定义为私有方法时会发生什么情况?
- 不实现父类的虚拟方法有什么风险
- 什么是直接 X 虚拟表?
- 有什么理由在没有继承的情况下声明一个虚拟方法
- 我是否错过了什么,或者虚拟呼叫的性能并不像人们所说的那样糟糕
- C++不支持非成员虚拟功能的原因是什么?
- 当超过可能的虚拟地址数时会发生什么情况
- 当声明了虚拟析构函数但没有实现时会发生什么情况
- 虚拟和非虚拟成员函数的调用方式有什么区别?
- 如果私有虚拟函数被覆盖为派生类中的公共函数,那么问题是什么
- 在什么条件下,纯虚拟方法生成了
- 在多级继承中派生的虚拟基类会发生什么
- 虚拟类和抽象类在C++有什么用
- 什么'这是实现;半虚拟'方法
- 虚拟析构函数的用途是什么