Linux下C++动态库的生成和使用

来源:互联网 发布:负面情绪 知乎 编辑:程序博客网 时间:2024/04/30 11:00

1. 导出函数的动态库

//DllTest.h#ifndef _DLLTEST_H#define _DLLTEST_Hextern "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 <iostream>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 <iostream>using namespace std;#include <dlfcn.h> #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为空。此时可通过两种方法解决这个问题:

  • export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./,假设动态库文件与可执行文件在同一目录下
  • 修改/etc/ld.so.conf文件,增加动态库所在目录,然后执行/sbin/ldconfig即可。

2. 导出类的动态库

Linux下动态库中导出类比较麻烦,可参考下例进行。说明一下,这个例子参考了http://blog.chinaunix.net/uid-26000296-id-3778641.html,代码略作了修改。

//Base.h#ifndef BASE_CLASS_H#define BASE_CLASS_Hclass 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 factoriestypedef 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 factoriesextern "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 <iostream>using namespace std;#include <dlfcn.h> #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_Hclass 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 factoriestypedef 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 factoriesextern "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 <iostream>using namespace std;#include <dlfcn.h> #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的析构函数如果缺失定义,也会出现类似错误。