精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
It is even more beneficial to apply the technique of abstract base classes to the scenario of binary packaging in DLLs. In the previous DLL sample, we had to export each function of the object, and we depended on the name-mangling scheme of the compiler. There is no standard for name-mangling, so you have to provide objects for each compiler that a potential client might want to use for their implementation.把抽象基类技术应用到DLL里的二进制包情况下也是有益的。在以前的DLL例子里,我们必须导出对象里每个函数,且我们依赖于编译器的命名排编。不存在标准的命名排编,所以你不得不提供为每个编译器服务的对象,潜在的客户端需要用什么编译器实现,你就要对应处理一下。
Remember that pure abstract base classes provide little more than a table of function entry points, and the entry points are allocated by the compiler of the object. They are initialized at load time and not freed until the module is unloaded. If we create object instances indirectly, memory management is not a problem. Code generated by a client's compiler can freely access all these tables through the vtable pointer, which is always located in the first 4 bytes of the memory image of the object. All C++ compilers use the same memory layout for their implementations of vtables, so vtables are actually a de facto binary standard in the C++ world.记住纯抽象基类只是提供了函数入口点的表,入口点是由编译器分配的。他们在加载时初始化且直到模块卸载时才释放。如果我们直接创建个对象实例,内存管理没有问题。客户端编译生成的代码能够自由存取vtable指针里的所有表,vtable始终在对象的内存映象的前4个字节里。所有C++的编译器使用相同的内存布局,所以vtables实际上是C++世界里的标准。
Thus, this technique lets you export C++ classes without exporting any function name. (16-bit Windows still requires the _export tag or compilation with a special compiler option, because we need prologue/epilogue code to switch between different data segments of DLL and EXE.) All that the client and the object have to agree on is the layout of the vtable—that is, the order of the functions in the vtable, the number and kind of parameters for each function, and the way the parameters are passed (in most cases on the stack: calling convention!).这个技术让你导出C++类而不用导出任何函数名称。客户端和对象在使用vtable时必须一致—也就是说,在vtable里的顺序,每个函数里参数的个数和类型,参数传递的类型(通常是调用栈:调用约定)
The layout of the vtable and the function parameters is completely defined by an abstract base class, so that a C++ header file can server as a complete description of the syntax of an interface contract.vtable的布局和函数参数完全由抽象基类定义,所以C++头文件能够把接口描述的很清晰。
This sample is based on the first DLL subdirectory, DB_CPPDLL. The changes are almost identical to the ones for DB_VTBL, except that we already implemented the indirect instantiation of CDB for other reasons (memory management!). 这个例子是基于第一个DLL子目录下的例子DB_CPPDLL。修改几乎和DB_VTBL一样,除了出于其它原因我们已经实现的非直接实例化CDB对象。
The following code illustrates the resulting Interface\dbsrv.h header file:下面代码演示了Interface\dbsrv.h文件的修改结果
class IDB { // Interfaces public: // Interface for data access. virtual HRESULT Read(short nTable, short nRow, LPWSTR lpszData) =0; (...) }; class IDBSrvFactory { // Interface public: virtual HRESULT CreateDB(IDB** ppObject) =0; virtual ULONG Release() =0; }; HRESULT DEF_EXPORT DllGetClassFactoryObject(IDBSrvFactory ** ppObject);
The new Object\dbsrvimp.h header:新的Object\dbsrvimp.h头文件如下:
#include "..\Interface\dbsrv.h" class CDB : public IDB { // Interfaces public: // Interface for data access. HRESULT Read(short nTable, short nRow, LPWSTR lpszData); }; class CDBSrvFactory : public IDBSrvFactory { // Interface public: HRESULT CreateDB(IDB** ppObject); ULONG Release(); };
The resulting DBSrvFact.CPP (no changes, just adjusting the instantiation function): DBSrvFact.CPP结果如下(没有修改,只是调整了实例化函数):
HRESULT CDBSrvFactory::CreateDB(IDB** ppvDBObject) { *ppvDBObject=(IDB*) new CDB; return NO_ERROR; } (...) HRESULT DEF_EXPORT DllGetClassFactoryObject(IDBSrvFactory ** ppObject) { *ppObject=(IDBSrvFactory*) new CDBSrvFactory; return NO_ERROR; }
Everything works just as before. The performance is identical to that of the prior version of the code where everything is compiled into one executable! The compiler does the same indirect function call through the vtable.性能和上版本一样。编译器通过vtable实现了同样的非直接函数调用。
Run DumpBin.EXE /exports db.dll and you will see that the only exported entry point is DllGetClassFactoryObject! 调用DumpBin.EXE /exports db.dll,你会看到只有一个DllGetClassFactoryObject导出函数。
All the rest of the dangerous, mangled names have disappeared. All member functions are now cleanly accessed through vtables. We could even dare to load the DLL dynamically (through LoadLibrary/GetProcAddress), because there is only one exported entry point.其它危险,命名排编没有了。所有成员函数通过vtables清晰地访问了。我们甚至能够大胆地动态加载DLL(通过LoadLibrary/GetProcAddress),因为只有一个导出入口点了。