c++进阶5:动态库与静态库_类

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 创建

  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
  1. 生成静态库,并打包

注意:关于main,测试函数的内容不打包;

-参数含义
1ar打包
2r替换模块(replace)
-c创建库(create)
-s建立索引
3libarrayalg.a库名:前面必须有lib,后面必须有.a
4alg.o array.o打包的可执行程序,不包括main
ar -rcs libpoint3D.a Point3D.o Point3D_test.o

tar和ar都是归档工具

  • tar用于创建.tar归档文件。
  • ar用于创建归档文件,并且为归档的目标文件中的符号建立索引。
  1. 查看目标文件的符号(symbol)信息
nm 目标文件
nm libarrayalg.a
nm array.o
nm alg.o

目标文件可以是.o、.a,也可以是可执行文件。

  1. 链接静态库
    方案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 创建

  1. 编译目标文件
g++ -c -fPIC Point3D_test.cpp -o Point3D_test.o
  1. 生成动态库
   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 使用

  1. 生成可执行文件
 g++ -o Point3D_test Point3D_test.cpp -L. -lpoint3D

或者

  g++ -o Point3D_test Point3D_test.cpp ./libpoint3D.so

注意:库一定要放在命令行的末尾

  1. 测试
    指定动态链接库位置
 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。
具体实现步骤如下:

  1. 定义一个抽象类,提供纯虚函数接口。
  2. 具体实现类继承抽象类。
  3. 提供抽象类对象的创建和销毁的接口。

3.1 初级版本

3.1.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++的版本,目前不应该依赖该宏的值。
  1. 修改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)

  1. 重新编译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 使用

  1. 执行
./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
  1. 生成静态库
ar -rcs libtest.a test.o

4. 总结

在这里插入图片描述

静态库、共享库与动态库编译链接使用比较
在这里插入图片描述

静态库与动态库Make file比较

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值