C++通过动态链接库导出类,以及WINDOWS的静态链接库LIB和动态链接库DLL基本区别和使用

WINDOWS下的静态链接库LIB和动态链接库DLL基本区别和使用

自己理解和整理的:

1、纯静态库LIB,包含所有函数内容和地址,全部编入程序中,是CPP编译出的OBJ文件的简单链接。

2、动态库DLL编译生成的lib,lib只含函数入口供编译时静态链接使用(使用Win API loadlibrary动态加载dll可以不用这个lib),DLL含函数内容和地址,运行时加载。

3、纯静态库LIB似乎都比动态库DLL+动态库lib体积大。

4、静态库LIB的使用:

    静态库LIB直接使用IDE链接器设置链接,添加头文件,LIB即可完整嵌入程序中并使用。

    或者加入预编译指令#pragma comment (lib,"*.lib"),这种方法优点是可以利用条件预编译指令链接不同版本的LIB文件(比如DEBUG和RELEASE)。

5、动态库DLL的使用:

    DLL 可以通过隐式加载(加载时动态链接)和显式加载(运行时动态加载)使用。

    隐式加载为工程添加lib及头文件进行编译,并在程序开始运行时就需要加载DLL。

    显式加载需要知道DLL中导出的函数类型(依然是提供的头文件),使用Win API loadlibrary动态加载,在运行时加载及释放。

C++通过动态链接库导出类

通过接口类导出类(一般方法):

导出类是一个派生类,派生自一个只包含纯虚函数的抽象类(接口类)

DLL最少只需要提供一个用于获取类对象指针的接口。使用者跟DLL提供者共用一个抽象类的头文件,使用者依赖于DLL的东西很少,只需要知道抽象类的接口,以及获取对象指针的导出函数,对象内存空间的申请是在DLL模块中做的,释放也在DLL模块中完成,最后记得要调用释放对象的函数。

这种方式比较好,通用,产生的DLL没有特定环境限制。借助了C++类的虚函数。一般都是采用这种方式。除了对DLL导出类有好处外,采用接口跟实现分离,可以使得工程的结构更清晰,使用者只需要知道接口,而不需要知道实现。

简单的导出类

只需要在导出类加上__declspec(dllexport),就可以实现导出类。对象空间还是在使用者的模块里,dll只提供类中的函数代码。不足的地方是:使用者需要知道整个类的实现,包括基类、类中成员对象,也就是说所有跟导出类相关的东西,使用者都要知道。通过Dependency Walker可以看到,这时候的dll导出的是跟类相关的函数:如构造函数、赋值操作符、析构函数、其它函数,这些都是使用者可能会用到的函数。

这种导出类的方式,除了导出的东西太多、使用者对类的实现依赖太多之外,还有其它问题:必须保证使用同一种编译器。导出类的本质是导出类里的函数,因为语法上直接导出了类,没有对函数的调用方式、重命名进行设置,导致了产生的dll并不通用。

DLL头文件dlltest.h

#pragma once
#include <iostream>
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

class DLL_API ExportInterface
{
public:
	virtual void foo() = 0;
	virtual ~ExportInterface()
	{
		std::cout << "call ~ExportInterface"<< std::endl;
	}
};

extern "C" DLL_API ExportInterface* getInstance();
extern "C" DLL_API void releaseInstance(ExportInterface* pInstance);

#ifdef DLL_EXPORTS  //我们并不需要向外导出该类的定义,在外部代码编译时,也不需要包含此类的定义。
class ExportClass : public ExportInterface
{
private:
	std::string x; //由于外部代码对此不可见,此处的std::string是安全的。
public:
	void foo(); //函数体在dllExample.cpp中实现
	virtual ~ExportClass()
	{
		std::cout << "call ~ExportClass" << std::endl;
	}
};
#endif

DLL源文件dlltest.cpp

#define DLL_EXPORTS
#include "dlltest.h"
#include <iostream>

extern "C" DLL_API ExportInterface* getInstance()
{
	ExportInterface* pInstance = new ExportClass();
	return pInstance;
}

extern "C" DLL_API void releaseInstance(ExportInterface* pInstance)
{
	pInstance->~ExportInterface();
}

void ExportClass::foo()
{
	std::cout << "call func foo" << std::endl;
	//do something...
	return;
}

调用方法:

#include <iostream>
#include <windows.h>
#include "dlltest.h"
using namespace std;
// 动态加载DLL
// 函数指针,用于获取GetProcAddress返回的函数地址,并调用DLL中的函数
typedef ExportInterface* (*DllGetInstance)(void);
typedef void (*DllReleaseInstance)(ExportInterface*);

int main()
{
	DllGetInstance getInstance;
	DllReleaseInstance releaseInstance;
	ExportInterface* pTest;
	// 显式加载
	HINSTANCE hInstLibrary = LoadLibrary("./TestDll.dll");

	if (hInstLibrary == NULL)
	{
		FreeLibrary(hInstLibrary);
		cout << "cant load dll" << endl;
	}
	getInstance = (DllGetInstance)GetProcAddress(hInstLibrary, "getInstance");
	releaseInstance = (DllReleaseInstance)GetProcAddress(hInstLibrary, "releaseInstance");
	if (getInstance == NULL || releaseInstance == NULL)
	{
		FreeLibrary(hInstLibrary);
		cout << "cant get func" << endl;
	}
	
	pTest = getInstance();
	pTest->foo();
	releaseInstance(pTest);

	std::cin.get();

	FreeLibrary(hInstLibrary);

	std::cin.get();
	return 0;
}

参考:

https://www.cnblogs.com/405845829qq/p/4108450.html

DLL动态库的创建,隐式加载和显式加载:

https://blog.csdn.net/dcrmg/article/details/53437913

DLL导出类:

https://www.cnblogs.com/JingJ/p/4442286.html

C++ DLL导出类 知识大全:

https://www.cnblogs.com/lidabo/p/7121745.html

猜你喜欢

转载自blog.csdn.net/fantasysolo/article/details/88553742