封装库学习心得
以前用别人的框架API函数,只能看到一个接口类,这个接口类里面全是纯虚的方法,但不明白是如何实现这些功能的,现在狠下心将其研究清楚。
封装库按照大类可以分为两种,即动态链接库和静态库
而动态链接库又分为两种加载的方式,动态加载和静态加载。
下面我就讲讲这这些库的具体创建和具体应用。
一,动态库的创建
我用的vs2003编译工具,我以游戏大厅房间桌子的一些接口为例,具体创建一个动态库的步骤如下
1,新建项目,选择win32项目(不是win32控制台程序),然后在设置里面选择DLL,完成。
新建一个头文件,这个头文件名字叫IRoomTable.h,在头文件里面写一个纯虚类,如下
class IRoomTable
{
public:
virtual ~IRoomTable(){};
virtual int GetRoomID()=0;
virtual unsigned long getGameID()=0; // 游戏id
virtual const char* getGameName()=0; // 游戏名字
virtual unsigned long getRoomID()=0; // 游戏房间id
virtual const char* getRoomName()=0; // 游戏房间名字
virtual unsigned short getTableID()=0; // 桌子ID,从1开始
virtual const char* getTableName()=0;// 桌子名字
};
2,新建一个头文件,文件名叫RoomTable.h,这个头文件里面写一个类RoomTable,继承于IRoomTable,具体实现如下
#pragma once
#include "IRoomTable.h"
class RoomTable :
public IRoomTable
{
public:
RoomTable(void);
virtual ~RoomTable(void);
int GetRoomID();
virtual unsigned long getGameID(); // 游戏id
virtual const char* getGameName(); // 游戏名字
virtual unsigned long getRoomID(); // 游戏房间id
virtual const char* getRoomName(); // 游戏房间名字
virtual unsigned short getTableID(); // 桌子ID,从1开始
virtual const char* getTableName(); // 桌子名字
private:
char *m_gamename;
char *m_roomname;
char *m_tablename;
};
3,再新建一个RoomTable.cpp文件,这个文件里是RoomTable类的实现,代码如下
#include "StdAfx.h"
#include "./roomtable.h"
RoomTable::RoomTable(void)
{
m_gamename = new char[20];
m_roomname = new char[20];
m_tablename = new char[20];
}
RoomTable::~RoomTable(void)
{
delete m_gamename;
delete m_roomname;
delete m_tablename;
}
int RoomTable::GetRoomID()
{
return 13141;
}
unsigned long RoomTable::getGameID()
{
return 13141;
}
const char* RoomTable::getGameName()
{
strcpy(m_gamename,"BaiShanMJ");
return m_gamename;
}
unsigned long RoomTable::getRoomID()
{
return 13141;
}
const char* RoomTable::getRoomName()
{
m_roomname = new char[20];
strcpy(m_roomname,"BaiShanMJ");
return m_roomname;
}
unsigned short RoomTable::getTableID()
{
return 13141;
}
const char* RoomTable::getTableName()
{
m_tablename = new char[20];
strcpy(m_tablename,"BaiShanMJ");
return m_tablename;
}
4,到现在为止,我们已经实现了RoomTable这个类,现在,我们要做的就是对外开放一个创建这个类对象的接口,也就是要将我们实现的类导出去。具体做法如下
(1)新建一个头文件IExport.h,在这个头文件里实现如下
#ifndef IROOMTABLE_IROOMTABLE
#define IROOMTABLE_IROOMTABLE
class IRoomTable;
extern "C" __declspec( dllexport ) IRoomTable* CreateRoomTable();
extern "C" __declspec( dllexport ) void RealseRoomTable(IRoomTable* roomtable);
#endif
注意,这里的CreateRoomTable没有带任何参数,那是因为我们这个类的构造函数是无参构造函数,如果是有参数的构造函数,那么就要加入相对应的参数。
(2)在工程的DllMain的主文件里加入两个导出接口的实现,当然,可以在任何一个cpp中来实现这个,我们只不过选的是DllMain文件里。
#include "stdafx.h"
#include "IExport.h"
#include "RoomTable.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
extern "C" __declspec( dllexport ) IRoomTable* CreateRoomTable()
{
return new RoomTable();
}
extern "C" __declspec( dllexport ) void RealseRoomTable(IRoomTable* logicengine)
{
if ( logicengine )
{
delete logicengine;
}
}
好了,到目前为止,我们的动态库就完成了,包括里面类的实现,包括类的导出,都可以实现了。这时编译,就可以在Debug下面看到一个以dll结尾的动态库,要设置动态库的生成名称,可以在项目的设置属性里面修改。
二,动态库的加载及应用
动态加载
首先应该将动态库放到新建的工程的工作目录下,一般是Debug目录
1,动态加载
这里是函数指针类型,通过CreateLogicEngine这种类型就可以来创建一个函数指针。
typedef IRoomTable* (*CreateLogicEngine)();
HINSTANCE m_hinstace;
获取加载的动态库的实例句柄
m_hinstace = LoadLibrary("RoomTableDll.dll");
if (!m_hinstace)
{
return false;
}
通过函数指针定义一个函数
CreateLogicEngine _CreateLogicEngine;
在动态库中寻找CreateRoomTable这个函数,并将其地址返回给_CreateLogicEngine,当然,别忘了强制类型装换。
_CreateLogicEngine = (CreateLogicEngine)GetProcAddress(m_hinstace, "CreateRoomTable");
用_CreateLogicEngine来创建对象,这样就可以用m_le来调用动态库里面的函数了。
IRoomTable *m_le = _CreateLogicEngine();
2,静态加载
(1)如果要静态加载,那么要将和动态库一起生成的一个lib文件也放到工作目录下(注意,这个lib里面只不过是包含了动态库里面的函数的映射,通过这个函数映射可以找到动态库里面的具体实现,这个lib和我们创建一个静态库项目所生成的静态库是不一样的,后面会讲到)
(2)第二步是将静态库加载到程序工程中来,可以在附加依赖项中来加,也可以用
#pragma comment(lib, "RoomTableDll.lib")来加。
(3)将#include "IRoomTable.h"虚接口文件
和#include "IExport.h"导出文件加入到工程中
并在应用的地方包含这两个头文件。
这些工作都做好以后,就可以这样在创建动态库中类的对象。
IRoomTable *m_le = CreateRoomTable();
这里再也不用用函数指针来加载,而是直接用动态库中的导出接口函数CreateRoomTable
3,应用
所谓应用就是直接用m_le指针对象来直接调用动态库类中的接口。实例如下
int gameID = m_le->getGameID();
cout<<"gameID:"<<gameID<<endl;
cout<<"RoomID:"<<m_le->GetRoomID()<<endl;
cout<<"TableID:"<<m_le->getTableID()<<endl;
const char *gamename = m_le->getGameName();
cout<<"gamename:"<<gamename<<endl;
cout<<"roomname:"<<m_le->getRoomName()<<endl;
cout<<"tablename:"<<m_le->getTableName()<<endl;
cout<<m_le->getRoomName()<<endl;
三,静态库创建
说道静态库,那么就比较简单,其实现和调用和动态库基本上差不多
1, 创建一个win32项目(不是win32控制台程序),然后在设置里面选择静态库lib
2, 接下来和动态库的创建基本上一样,只是不用创建导出文件IExport.h了,也不用在IExport.h对接口函数进行实现了,但是要在虚接口文件IRoomTable.h的文件中添加创建对象的接口
IRoomTable* Createtable();
void ReleaseTable(IRoomTable* table);
并随便在一个cpp中对其进行实现,譬如就在RoomTable.cpp中对其进行实现
IRoomTable* Createtable()
{
return new RoomTable();
}
void ReleaseTable(IRoomTable* table)
{
if (table)
{
delete table;
}
}
这样,一个静态库就创建好了
四,静态库的加载及应用
(1) 首先,要将编译生成的静态库lib文件放到工作目录下面,然后在附加依赖项中添加这个lib文件或者用#pragma comment(lib, "*.lib")来加载
(2) 第二步是将动态库中的虚接口文件IRoomTable.h包含到工程中来,然后直接就用虚接口中的Createtable方法来创建对象,并返回给基类指针。
IRoomTable *m_le= Createtable();
这样就可以直接用m_le来调用静态库中的类成员函数了。
注:动态库的加载好像还是有点问题,后续再完善用例。