C++导出类
1.纯虚函数方式
纯虚函数定义如下(mysqldll.h)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #pragma once #ifdef MMYSQL_EXPORTS #define MMYSQL_API __declspec(dllimport) #else #define MMYSQL_API __declspec(dllexport) #endif
#include "stdafx.h" #include <string> #include <mysql.h> #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "libmysql.lib")
class mmysql { public: virtual bool initConnection() = 0; virtual bool addData(char* username, char* password) = 0; virtual bool deleteData(char* username) = 0; virtual bool isExistUser(char* username) = 0; virtual bool isMatch(char* userName, char* passWord) = 0; virtual void Release() = 0; }; extern "C" MMYSQL_API mmysql* _stdcall CreateMySqlObj(); extern "C" MMYSQL_API void _stdcall DestroyMySqlObj(mmysql* pMmysql);
|
导出类定义如下(mysqlclass.h)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #pragma once #include "MySqlDll.h"
class MMYSQL_API mysqlDLL : public mmysql { public: mysqlDLL(); mysqlDLL(std::string hosts, std::string user, std::string pswd, std::string table, int port); ~mysqlDLL(); public: virtual bool initConnection(); virtual bool addData(char* username, char* password); virtual bool deleteData(char* username); virtual bool isExistUser(char* username); virtual bool isMatch(char* userName, char* passWord); virtual void Release() {} private: int _port; std::string _user; std::string _pswd; std::string _hosts; std::string _table; MYSQL _mySQL; bool _isConnection; };
|
.def文件如下
1 2 3 4
| LIBRARY "ConsoleApplication4"(dll名称,自行更换) EXPORTS CreateExportObj @ 1 DestroyExportObj @ 21234
|
这里提供 CreateMySqlObj()和DestroyMySqlObj(mmysql* pMmysql)的定义:
1 2 3 4 5 6 7 8 9
| MMYSQL_API mmysql* APIENTRY CreateMySqlObj() { return new mysqlDLL; }
MMYSQL_API void APIENTRY DestroyMySqlObj(mmysql* pMmysql) { pMmysql->Release(); }
|
调用示例如下:
1 2 3 4 5 6 7 8 9 10 11 12
| #include "stdafx.h" #include "MySqlDll.h" #pragma comment(lib, "../debug/ConsoleApplication4.lib")
int main() { mmysql* m = CreateMySqlObj(); m->initConnection(); m->addData("张三", "38459");
return 0; }
|
2.常规方法
导出类定义如下(mysqlclass.h)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #pragma once #ifdef MMYSQL_EXPORTS #define MMYSQL_API __declspec(dllimport) #else #define MMYSQL_API __declspec(dllexport) #endif
class MMYSQL_API mysqlDLL { public: mysqlDLL(); ~mysqlDLL(); public: void Hello(); };
|
mysqlclass.cpp
1 2 3 4 5 6 7 8 9 10 11
| mysqlDLL::mysqlDLL() {
} mysqlDLL::~mysqlDLL() {
} void mysqlDLL::Hello() { }
|
调用示例如下:
1 2 3 4 5 6 7 8 9 10
| #include "stdafx.h" #include "mysqlclass.h" #pragma comment(lib, "../debug/ConsoleApplication4.lib")
int main() { mysqlDLL* m = new mysqlDLL(); m->Hello(); return 0; }
|
C++导出标准库模板类的实例化
在有些情况需要导出模板类的实例化,否则会产生类似以下的Warning
1 2 3
| warning C4251: “XXX”: class“std::map<_Kty,_Ty>”需要有 dll 接口由 class“XXX”的客户端使用 warning C4251: “XXX”: class“std::vector<_Kty,_Ty>”需要有 dll 接口由 class“XXX”的客户端使用
|
以导出std::map<int, int>类型为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| template class __declspec(dllexport) std::allocator<std::pair<const int, int> >; template class __declspec(dllexport) std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >; class __declspec(dllexport) MyClass { MyClass(){} ~MyClass(){} void appendElement(int key, int val) { m_map.insert(std::pair<int, int>(key, val)); } private: std::map<int, int> m_map; };
|
假设我们有一个模板类如下:
1 2 3 4 5 6 7 8 9 10
| template< class T > class CArray { public: CArray(); private: T *m_pData; }
template< class T > CArray< T >::CArray() { m_pData = NULL; }
|
如果在dll中包含了该模板类,要是不导出的话,当别的dll工程使用该模版类作为成员变量时,编译就会出现出现警告,提示该模板类没有导出供客户端程序使用。如果按通常的dll接口导出方式导出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #ifndef ALGORITHM_EXPORTS #define ALGORITHM_CLASS __declspec(dllimport) #else #define ALGORITHM_CLASS __declspec(dllexport) #endif
template< class T > class ALGORITHM_CLASS CArray { public: CArray(); private: T *m_pData; }
template< class T > CArray< T >::CArray() { m_pData = NULL; }
|
当其他程序通过dll调用该模版类的时候,编译又会报错,提示写在模版类声明外面的函数不能__declspec(dllimport)。如果把模版类声明外面的函数写到模板类声明里面去实现,编译可以通过,而在链接时又会出错,提示导出的模板类的函数是无法解析的外部符号。总之,用通常的方法都会产生问题,这是因为模板类是动态编译的,它并不参与dll的编译,只会在使用它的程序中才编译,而在使用它的程序中又不允许导入模板类。
为了解决这个问题,只能从导出宏的定义上动手。如果对dll该模板类是__declspec(dllexport)而对其他程序既不导出也不导入,就可以避免上述的错误。因此程序可以修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #ifndef ALGORITHM_EXPORTS #define ALGORITHM_CLASS __declspec(dllimport) #define ALGORITHM_TEMPLATE #else #define ALGORITHM_CLASS __declspec(dllexport) #define ALGORITHM_TEMPLATE __declspec(dllexport) #endif
template< class T > class ALGORITHM_TEMPLATE CArray { public: CArray(); private: T *m_pData; }
template< class T > CArray< T >::CArray() { m_pData = NULL; }
|
如上所述,只要对模版类使用ALGORITHM_TEMPLATE宏而不使用ALGORITHM_CLASS,就可以完美的解决模板类导出困难的问题。