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 //EXPORT
#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 //EXPORT
#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,就可以完美的解决模板类导出困难的问题。