1. 导出函数的动态库
//DllTest.h
#ifndef _DLLTEST_H
#define _DLLTEST_H
extern "C" int add(int a,int b);
typedef int (*add_t)(int a,int b);
#endif
//DllTest.cc
#include "DllTest.h"
int add(int a,int b)
{
return a+b;
}
上述动态库需要导出函数add。编译生成动态库:g++ -fPIC -shared -o libDllTest.so DllTest.cc
动态库加载方式分为静态和动态加载。
静态方式加载动态库示例:
//main.cc
#include
using namespace std;
#include "DllTest.h"
int main(int argc,char** argv)
{
cout << "------------static call-------------" << endl;
cout << "add(5,3) = " << add(5,3) << endl;
}
编译可执行程序:g++ -o main main.cc -lDllTest -L. /
动态方式加载动态库示例:
//main.cc
#include
using namespace std;
#include
#include "DllTest.h"
int main(int argc,char** argv)
{
cout << "------------dynamic call-------------" << endl;
void *so_handle = dlopen("libDllTest.so", RTLD_LAZY); // 载入.so文件
if (!so_handle) {
cout << "Error: load so `failed." << endl;
return -1;
}
add_t fn = (add_t)dlsym(so_handle, "add"); // 载入函数
if (NULL == fn) {
cout << "get function address failed" << endl;
return -1;
}
cout << "add 57 + 3 = " << fn(57, 3) << endl; // 调用函数
dlclose(so_handle); // 关闭so句柄
}
编译可执行程序:g++ -o main main.cc -ldl
extern “C”的作用不用多说了,但是需要说明的一点是:静态加载方式下可以不需要这个,但是动态加载方式下是必需的。
即使两种加载方式编译时都顺利通过,在运行时仍然可能出现找不到库的情况,此时静态方式会提示类似“./main: error while loading shared libraries: libDllTest.so: cannot open shared object file: No such file or directory”这样的错误,动态方式会导致so_handle为空。此时可通过两种方法解决这个问题:
2. 导出类的动态库
Linux下动态库中导出类比较麻烦,可参考下例进行。说明一下,这个例子参考了http://blog.chinaunix.net/uid-26000296-id-3778641.html,代码略作了修改。
//Base.h
#ifndef BASE_CLASS_H
#define BASE_CLASS_H
class BaseClass {
protected:
int member_var;
public:
BaseClass(): member_var(0) {}
virtual ~BaseClass() {}
void set_member_var(int param) {
member_var = param;
}
virtual int get_member_var() const = 0;
};
// the types of the class factories
typedef BaseClass* create_t();
typedef void destroy_t(BaseClass*);
#endif
//SubClass.cc
#include "Base.h"
class SubClass : public BaseClass {
public:
virtual int get_member_var() const {
return member_var;
}
};
// the class factories
extern "C" BaseClass* create() {
return new SubClass;
}
extern "C" void destroy(BaseClass* p) {
delete p;
}
编译生成动态库:g++ -fPIC -shared -o libSubClass.so SubClass.cc
加载动态库示例代码:
//main.cc
#include
using namespace std;
#include
#include "Base.h"
int main(int argc,char** argv)
{
void *so_handle = dlopen("libSubClass.so", RTLD_LAZY); // 载入.so文件
if (!so_handle) {
cout << "Error: load so failed." << endl;
return -1;
}
create_t* create = (create_t*)dlsym(so_handle, "create"); // 载入函数
if (NULL == create) {
cout << "get function address failed" << endl;
return -1;
}
destroy_t* destroy = (destroy_t*) dlsym(so_handle, "destroy");
if (NULL == destroy) {
cout << "get function address failed" << endl;
return -1;
}
BaseClass* pObj = create();
pObj->set_member_var(10);
cout << "pObj->get_member_var():" << pObj->get_member_var() << endl;
destroy(pObj);
dlclose(so_handle); // 关闭so句柄
}
编译可执行程序:g++ -o main main.cc -ldl。很明显,上述代码采用了动态加载方式。其实导出类的动态库也可以采用静态加载方式,示例代码如下:
//Base.h
#ifndef BASE_CLASS_H
#define BASE_CLASS_H
class BaseClass {
protected:
int member_var;
public:
BaseClass(): member_var(0) {}
virtual ~BaseClass() {}
void set_member_var(int param) {
member_var = param;
}
virtual int get_member_var() const = 0;
};
// the types of the class factories
typedef BaseClass* create_t();
typedef void destroy_t(BaseClass*);
#endif
//SubClass.h
#include "Base.h"
class SubClass : public BaseClass {
public:
SubClass(){};
virtual ~SubClass(){};
virtual int get_member_var() const;
};
// the class factories
extern "C" BaseClass* create() {
return new SubClass;
}
extern "C" void destroy(BaseClass* p) {
delete p;
}
//SubClass.cc
#include "SubClass.h"
int SubClass::get_member_var() const {
return member_var;
}
编译生成动态库:g++ -fPIC -shared -o libSubClass.so SubClass.cc
静态加载示例代码如下:
//main.cc
#include
using namespace std;
#include
#include "SubClass.h"
int main(int argc,char** argv)
{
BaseClass* pObj = create();
pObj->set_member_var(10);
cout << "pObj->get_member_var():" << pObj->get_member_var() << endl;
destroy(pObj);
}
编译可执行程序:g++ mainClass.cc -o mainClass -L./ -lSubClass
个人觉得静态加载方式明显的比动态加载方式麻烦,而且通用性不好,所以不建议使用。
上述代码中需要注意一个细节问题:如果SubClass.h中不定义、只声明SubClass的析构函数,虽然生成动态库时不会出错,但是生成可执行文件时,会报下列类似错误:
In function `SubClass::SubClass()':
mainClass.cc:(.text._ZN8SubClassC2Ev[_ZN8SubClassC5Ev]+0x1f): undefined reference to `vtable for SubClass'
这个错误是一个比较常见的错误,一般原因都是由于在继承层次的类中,virtual函数没有定义、只有声明。比如上例中的Base.h中,如果BaseClass的析构函数如果缺失定义,也会出现类似错误。