0.前提(文件内容)
- Point3D.h
#ifndef __POINT3D_H
#define __POINT3D_H
// 定义一个抽象类
// 提供纯虚函数接口
class IPoint3D{
public:
virtual void Print() const = 0;
virtual ~IPoint3D(){}
};
// 具体实现类要继承抽象类
class Point3D:public IPoint3D{
public:
int x,y,z;
Point3D(int x,int y,int z);
void Print()const;
};
// 提供抽象类对象的创建和销毁的接口
extern "C"{ // 因为只支持C++,所以没有使用__cplusplus条件编译
IPoint3D* CreatePoint3D(int x,int y,int z);
void DestroyPoint3D(IPoint3D* p);
typedef IPoint3D* (*create_t)(int,int,int);
typedef void (*destroy_t)(IPoint3D*);
}
#endif // __POINT3D_H
- Point3D.cpp
#include "Point3D.h"
#include <iostream>
using namespace std;
Point3D::Point3D(int x,int y,int z):x(x),y(y),z(z){}
void Point3D::Print()const{
cout << "(" << x << "," << y<<"," << z<< ")" << endl;
}
IPoint3D* CreatePoint3D(int x,int y,int z){
return new Point3D(x,y,z);
}
void DestroyPoint3D(IPoint3D* p){
delete p;
}
- Point3D_test.cpp
#include <iostream>
#include "Point3D.h"
using namespace std;
int main(){
Point3D point(1,2,3);
point.Print();
// 成员变量在对象中的偏移量
printf("%d %d %d\n",&Point3D::x,&Point3D::y,&Point3D::z);
cout << &Point3D::x << ' ' << &Point3D::y << ' ' << &Point3D::z << endl;
// cout << reinterpret_cast<long>(&Point3D::x) << ' ' << reinterpret_cast<long>(&Point3D::y) << ' ' << reinterpret_cast<long>(&Point3D::z) << endl;
printf("%p\n",&point);
printf("%p %p %p\n",&point.x,&point.y,&point.z);
// &类名::成员变量 获取成员变量在对象中的偏移量
// &对象.成员变量 获取成员变量的地址
cout << (void*)&Point3D::Print << endl;
cout << reinterpret_cast<void*>(&Point3D::Print) << endl;
// cout << reinterpret_cast<void*>(Point3D::Print) << endl; // 成员函数获取地址必须加上&
}
1. 静态库的制作与使用
1.1 创建
- 编译源文件
注意:-c(排除main函数的影响,最好用),
-o的用法,前面是结果(.o),后面是源文件(.cpp)
g++ -c -o Point3D.o Point3D.cpp
g++ -c -o Point3D_test.o Point3D_test.cpp -std=c++11
- 生成静态库,并打包
注意:关于main,测试函数的内容不打包;
- | 参数 | 含义 |
---|---|---|
1 | ar | 打包 |
2 | r | 替换模块(replace) |
- | c | 创建库(create) |
- | s | 建立索引 |
3 | libarrayalg.a | 库名:前面必须有lib,后面必须有.a |
4 | alg.o array.o | 打包的可执行程序,不包括main |
ar -rcs libpoint3D.a Point3D.o Point3D_test.o
tar和ar都是归档工具
- tar用于创建.tar归档文件。
- ar用于创建归档文件,并且为归档的目标文件中的符号建立索引。
- 查看目标文件的符号(symbol)信息
nm 目标文件
nm libarrayalg.a
nm array.o
nm alg.o
目标文件可以是.o、.a,也可以是可执行文件。
- 链接静态库
方案1:带编译的结果的名字;注意:(1)不带前后缀(2)-L -l写在后面; (3)-L当前路径的.不能丢
g++ -o Point3D Point3D.o -L. -lpoint3D #方案1:带编译的结果的名字;注意:(1)不带前后缀(2)-L -l写在后面; (3)-L当前路径的.不能丢
方案2:带编译的结果的名字;
g++ Point3D.o -L. -lpoint3D #方案2:带编译的结果的名字;
方案3:直接写路径
g++ Point3D.o ./libpoint3D.a #方案3:直接写路径
注意:库一定要放在命令行的末尾
- 测试
./a.out
./Point3D
2. 共享库的制作
2.1 创建
- 编译目标文件
g++ -c -fPIC Point3D_test.cpp -o Point3D_test.o
- 生成动态库
g++ -shared Point3D_test.o -o libpoint3D.so
以上两步可以合并为g++ -shared -fPIC -o libtest.so test.cpp
- 选项说明
命令选项 | 作用 |
---|---|
shared | 创建动态库 |
fPIC | 代码都是与位置无关的 |
每个共享函数库都有个特殊的名字,称作soname。soname名字命名必须以lib作为前缀,然后是函数库的名字,然后是.so,最后是版本号信息。不过有个特例,就是非常底层的C库函数都不是以lib开头这样命名的。
2.2 使用
- 生成可执行文件
g++ -o Point3D_test Point3D_test.cpp -L. -lpoint3D
或者
g++ -o Point3D_test Point3D_test.cpp ./libpoint3D.so
注意:库一定要放在命令行的末尾
- 测试
指定动态链接库位置
export LD_LIBRARY_PATH=动态链接库位置
export LD_LIBRARY_PATH=.
执行
./point3D_test
关于动态链接库的安装路径
如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。
如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:
编辑/etc/ld.so.conf文件,加入库文件所在目录的路径
运行ldconfig ,该命令会重建/etc/ld.so.cache文件
当静态库和动态库同名时, gcc命令将优先使用动态库。
- 查看执行文件链接的动态链接库
ldd 可执行文件
也可以查看动态链接库所链接的其它动态库。
3. 动态加载库
topic:前面两种都属于静态链接库,需要重启更新;第三种属于动态连接,需要手动处理,需要的时候加载,不需要的时候,可以不用
类的动态链接库是利用多态性动态加载类,只能用于C++调用,不能用于C。
具体实现步骤如下:
- 定义一个抽象类,提供纯虚函数接口。
- 具体实现类继承抽象类。
- 提供抽象类对象的创建和销毁的接口。
3.1 初级版本
3.1.1 创建
- 修改Point3D.h
(1.1) 定义一个抽象类
// 提供纯虚函数接口
class IPoint3D{
public:
virtual void Print() const = 0;
virtual ~IPoint3D(){}
};
(1.2)具体实现类要继承抽象类
class Point3D:public IPoint3D{
public:
int x,y,z;
Point3D(int x,int y,int z);
void Print()const;
};
(1.3)提供抽象类对象的创建和销毁的接口
extern "C"{ // 因为只支持C++,所以没有使用__cplusplus条件编译
IPoint3D* CreatePoint3D(int x,int y,int z);
void DestroyPoint3D(IPoint3D* p);
typedef IPoint3D* (*create_t)(int,int,int);
typedef void (*destroy_t)(IPoint3D*);
}
- 完整案例
#ifndef __POINT3D_H
#define __POINT3D_H
//(1) 定义一个抽象类
// 提供纯虚函数接口
class IPoint3D{
public:
virtual void Print() const = 0;
virtual ~IPoint3D(){}
};
// 具体实现类要继承抽象类
class Point3D:public IPoint3D{
public:
int x,y,z;
Point3D(int x,int y,int z);
void Print()const;
};
// 提供抽象类对象的创建和销毁的接口
extern "C"{ // 因为只支持C++,所以没有使用__cplusplus条件编译
IPoint3D* CreatePoint3D(int x,int y,int z);
void DestroyPoint3D(IPoint3D* p);
typedef IPoint3D* (*create_t)(int,int,int);
typedef void (*destroy_t)(IPoint3D*);
}
#endif // __POINT3D_H
- extern关键字:在一个项目中必须保证函数、变量、枚举等在所有的源文件中保持一致,除非你指定定义为局部的。
extern "C"指令中的C,表示的一种编译和连接规约,而不是一种语言。C表示符合C语言的编译和连接规约的任何语言
extern "C"的真实目的是实现类C和C++的混合编程。在C++源文件中的语句前面加上extern “C”,表明它按照类C的编译和连接规约来编译和连接,而不是C++的编译的连接规约。 - __cplusplus的值是为了表示C++的版本,目前不应该依赖该宏的值。
- 修改Point3D_test.cpp
2.0.头文件
#include <dlfcn.h> //0.头文件
2.1 采用句柄的手段(智能指针,函数指针的用法)
if(NULL == so_handle){
cout << dlerror() << endl;
}
create_t create = (create_t)dlsym(so_handle,"CreatePoint3D");
destroy_t destroy = (destroy_t)dlsym(so_handle,"DestroyPoint3D");
dlclose(so_handle);
so_handle = NULL;
2.2 采用句柄的手段(智能指针,函数指针的用法)+强制类型转化
int (*sum)(int*, int) = (int(*)(int*,int))dlsym(so_handle,"sum");
if(NULL == so_handle){
cerr << "sum load error" << endl;
return 1;}
- 完整代码
#include <iostream>
#include <dlfcn.h>
#include "Point3D.h"
using namespace std;
int main(){
void* so_handle = dlopen("./libPoint3D.so",RTLD_LAZY);
if(NULL == so_handle){
cout << dlerror() << endl;
}
create_t create = (create_t)dlsym(so_handle,"CreatePoint3D");
destroy_t destroy = (destroy_t)dlsym(so_handle,"DestroyPoint3D");
IPoint3D* p = create(1,2,3);
p->Print();
destroy(p);
dlclose(so_handle);
}
3.1.2 两种情况
3.1.2.1 (命令行)
g++ -fPIC Ponit3D.cpp -c
g++ -shared Ponit3D.o -o libPonit3D.so
g++ Ponit3D_test2.cpp -ldl
./a.out
3.1.2.2 (makefile)
- 重新编译dynamic_makefile
3.0 最后的结果
DEST = Point3D_test2
3.1 创建动态库的时候加参数
$(CXX) -shared -fPIC -o $(LIB) $^
3.2 编译的时候程序进行修改(-ldl)
$(CXX) $(CXXFLAGS) $< -o $@ -ldl
- 完整代码 makefile
OBJS = Point3D.o
LIB = libp3d.so
DEST = Point3D_test2
#CXXFLAGS = -g
.PHONY: all clean
all:$(DEST)
$(DEST):$(DEST).cpp $(LIB)
@echo 创建测试程序$(DEST)
$(CXX) $(CXXFLAGS) $< -o $@ -ldl
$(LIB):$(OBJS)
@echo 创建动态库$(LIB)
$(CXX) -shared -o $(LIB) $^
$(OBJS):%.o:%.cpp
@echo 编译$(OBJS)
$(CXX) $(CXXFLAGS) $< -fPIC -c -o $@
clean:
@echo 清空
rm -f $(DEST) $(OBJS) $(LIB)
然后编译 make all
make clean
3.1.3 使用
- 执行
./Point3D_test2
./a.out
3.2动态加载库的问题
出错 | 翻译 | 改正 |
---|---|---|
redefine xxx | 重复定义 | 不同的.cpp文件可能定义了相同的函数,需要手动删除,保留其中的一个; |
dump | 吐核 | (1)编译的时候: -g (2)函数名字写错,导致在动态库没有生成对应的库函数;可以用压淬后的文件名 |
can not find .so | 找不到库文件 export LD_LIBRARY_PATH=. |
g++ -c -o test.o test.cpp
- 生成静态库
ar -rcs libtest.a test.o
4. 总结
静态库、共享库与动态库编译链接使用比较
静态库与动态库Make file比较