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

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为空。此时可通过两种方法解决这个问题:

  • 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_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的析构函数如果缺失定义,也会出现类似错误。








你可能感兴趣的:(C++)