Loading

C++静态链接库、动态链接库的导出以及使用

1. 静态链接库导出以及使用


代码结构:

.
├── include
│   └── mylib.h
├── lib
├── out
└── src
    ├── main.cpp
    └── mylib.cpp

1.1 linux

1.1.1 mylib.h

#pragma once
// 只需要声明类和函数即可
class Mylib_I {
public:
    virtual void print_x() const = 0;
    virtual void set_x(int) = 0;
    virtual ~Mylib_I() = default;
};

class ImpMylib : public Mylib_I {
public:
    ImpMylib(int);
    void greet() const;
    virtual void print_x() const override;
    virtual void set_x(int) override;
    ~ImpMylib() = default;
private:
    int x;
};

1.1.2 mylib.cpp

#include <iostream>
#include "mylib.h"
// 实现头文件中的类以及函数
ImpMylib::ImpMylib(int x):x(x){};
void ImpMylib::print_x() const {std::cout << this->x << std::endl;};
void ImpMylib::set_x(int x){ this->x = x;};
void ImpMylib::greet() const {std::cout << "Hello Mylib!" << std::endl;}

1.1.3 main.cpp

测试用

#include <iostream>
#include "mylib.h"
int main()
{
    int x = 3;
    Mylib_I* obj = new ImpMylib(x);
    obj->print_x();
    obj->set_x(100);
    obj->print_x();
    ((ImpMylib *) obj)->greet();
    return 0;
}

1.1.4 导出静态链接库并使用

  1. 编译mylib.cpp成.o文件
g++ -c  ./src/mylib.cpp -o ./out/mylib.o -I include
  1. 将.o文件打包成静态链接库.a

使用ar命令(archive) rcs (replace create symbolize)

ar rcs ./lib/libmylib.a ./out/mylib.o
  1. 编译main.cpp, 需要包含刚刚生成的静态链接库以及头文件,不需要mylib.cpp文件
g++ ./src/main.cpp -o main -I include -L lib -l mylib
  1. 运行可执行程序
./main
# 输出:
# 3
# 100
# Hello Mylib!

1.2 windows

windows和linux只有头文件不同,其余都是一样的。需要再函数或者类前面加一个关键词声明 __declspec(dllexport)
生成的静态链接库的后缀为lib

1.2.1 mylib.h

#pragma once
// 只需要声明类和函数即可
class __declspec(dllexport) Mylib_I {
public:
    virtual void print_x() const = 0;
    virtual void set_x(int) = 0;
    virtual ~Mylib_I() = default;
};

class __declspec(dllexport) ImpMylib : public Mylib_I {
public:
    ImpMylib(int);
    void greet() const;
    virtual void print_x() const override;
    virtual void set_x(int) override;
    ~ImpMylib() = default;
private:
    int x;
};

1.1.2 mylib.cpp

#include <iostream>
#include "mylib.h"
// 实现头文件中的类以及函数
ImpMylib::ImpMylib(int x):x(x){};
void ImpMylib::print_x() const {std::cout << this->x << std::endl;};
void ImpMylib::set_x(int x){ this->x = x;};
void ImpMylib::greet() const {std::cout << "Hello Mylib!" << std::endl;}

1.1.3 main.cpp

测试用

#include <iostream>
#include "mylib.h"
int main()
{
    int x = 3;
    Mylib_I* obj = new ImpMylib(x);
    obj->print_x();
    obj->set_x(100);
    obj->print_x();
    ((ImpMylib *) obj)->greet();
    return 0;
}

1.1.4 导出静态链接库并使用

  1. 编译mylib.cpp成.o文件
g++ -c  ./src/mylib.cpp -o ./out/mylib.o -I include
  1. 将.o文件打包成静态链接库.lib

使用ar命令(archive) rcs (replace create symbolize)

ar rcs ./lib/mylib.lib ./out/mylib.o
  1. 编译main.cpp, 需要包含刚刚生成的静态链接库以及头文件,不需要mylib.cpp文件
g++ ./src/main.cpp -o main.exe -I include -L lib -l mylib
  1. 运行可执行程序
main.exe
# 输出:
# 3
# 100
# Hello Mylib!



2. 动态链接库的导出以及使用

代码结构:

.
├── include
│   └── myDLL.h
├── dll
├── out
└── src
    ├── main.cpp
    └── myDLL.cpp

2.1 linux

2.1.1 myDLL.h

#pragma once
// 只需要声明函数以及类
class MyDLL_I {
public:
    virtual void print_x() const = 0;
    virtual void set_x(int) = 0;
    virtual ~MyDLL_I() = default;
};

class ImpMyDLL : public MyDLL_I {
public:
    ImpMyDLL(int);
    void greet() const;
    virtual void print_x() const override;
    virtual void set_x(int) override;
    ~ImpMyDLL();
private:
    int x;
};

extern "C" { // 用C的格式导出,C++由于函数重载会给函数起别名,之后可能找不到对应名字的函数
void print();
MyDLL_I* create_MyDLL_cls(int); // 类以函数接口的形式传递到外部
}

2.1.2 myDLL.cpp

#include<iostream>
#include "myDLL.h"

void print() {
    std::cout << "hello dll !" << std::endl;
}

MyDLL_I* create_MyDLL_cls(int x){
    MyDLL_I *p = new ImpMyDLL(x);
    return p;
}

ImpMyDLL::ImpMyDLL(int x):x(x){};
ImpMyDLL::~ImpMyDLL() {
    std::cout << "ImpMyDLL obj destroyed!" << std::endl;
}
void ImpMyDLL::print_x() const {std::cout << this->x << std::endl;};
void ImpMyDLL::set_x(int x){ this->x = x;};
void ImpMyDLL::greet() const {std::cout << "Hello MyDLL!" << std::endl;}

2.1.3 main.cpp

测试用

#include <iostream>
#include <dlfcn.h>
//linux系统需要这个头文件,windows系统需要windows.h头文件

#include "myDLL.h"
using  p_func = void(*)(); // 函数指针,返回类型为void
using create_func = MyDLL_I*(*)(int); // 函数指针,返回类型为MyDLL_I的指针

int main()
{
    int x = 3;
    void* hDLL = dlopen("./dll/myDLL.so", RTLD_LAZY);
//用相对目录,移动可执行文件后,如果在对应目录的dll目录下无so动态链接库则程序无法运行;
//而静态链接库生成的可执行文件移动到任何地方都能运行,但代价是可执行文件更大,如果一个文件会被多个app复用则推荐使用动态链接库,否则用静态链接库更合适。
    if (hDLL == nullptr) {
    std::cout << "NULL" << std::endl;
    } else {
        p_func func = (p_func)dlsym(hDLL, "print");
        create_func create = (create_func)dlsym(hDLL, "create_MyDLL_cls");
        MyDLL_I* obj = create(x);
        obj->print_x();
        obj->set_x(100);
        obj->print_x();
        delete obj;
        func();
    }
    dlclose(hDLL);
    return 0;
}

2.1.4 导出动态链接库并使用

  1. 导出so动态链接文件
g++ -shared -fpic -o ./dll/myDLL.so ./src/myDLL.cpp -I include
  1. 编译main.cpp, 动态加载动态链接库,只有运行到需要动态链接库时候才会去对应目录中查找并加载
g++ ./src/main.cpp -o main -I include
  1. 运行可执行程序
./main
# 输出:
# 3
# 100
# ImpMyDLL obj destroyed!
# hello dll !

2.2 windows

2.2.1 myDLL.h

windows和linux对应的动态链接库的头文件不同,前面需要写一段宏,要导出的对象前加上对应的宏,用来声明是否暴露在外,让外部调用这个接口。
主程序由于要使用windows.h头文件与linux的dlfcn.h有所不同。

#pragma once
// windows需要加这个关键词
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif

class MYDLL_API MyDLL_I {
public:
    virtual void print_x() const = 0;
    virtual void set_x(int) = 0;
    virtual ~MyDLL_I() = default;
};

class MYDLL_API ImpMyDLL : public MyDLL_I {
public:
    ImpMyDLL(int);
    void greet() const;
    virtual void print_x() const override;
    virtual void set_x(int) override;
    ~ImpMyDLL();
private:
    int x;
};

extern "C" {
void MYDLL_API print();
MyDLL_I* MYDLL_API create_MyDLL_cls(int);
}

2.1.2 myDLL.cpp

#include<iostream>
#include "myDLL.h"

void print() {
    std::cout << "hello dll !" << std::endl;
}

MyDLL_I* create_MyDLL_cls(int x){
    MyDLL_I *p = new ImpMyDLL(x);
    return p;
}

ImpMyDLL::ImpMyDLL(int x):x(x){};
ImpMyDLL::~ImpMyDLL() {
    std::cout << "ImpMyDLL obj destroyed!" << std::endl;
}
void ImpMyDLL::print_x() const {std::cout << this->x << std::endl;};
void ImpMyDLL::set_x(int x){ this->x = x;};
void ImpMyDLL::greet() const {std::cout << "Hello MyDLL!" << std::endl;}

2.1.3 main.cpp

测试用

#include <iostream>
#include <windows.h> // windows系统
#include "myDLL.h"
using  p_func = void(*)();
using create_func = MyDLL_I*(*)(int);
int main()
{
    int x = 3;
    HINSTANCE hDLL = LoadLibraryA("./dll/myDLL.dll"); // dll文件相对路径
    if (hDLL == nullptr) {
    std::cout << "NULL" << std::endl;
    } else {
        p_func func = (p_func)GetProcAddress(hDLL, "print");
        create_func create = (create_func)GetProcAddress(hDLL, "create_MyDLL_cls");
        MyDLL_I* obj = create(x);
        obj->print_x();
        obj->set_x(100);
        obj->print_x();
        delete obj;
        func();
    }
    FreeLibrary(hDLL);
    return 0;
}

2.1.4 导出动态链接库并使用

  1. 导出dll动态链接文件
g++ -shared -fpic -o ./dll/myDLL.dll ./src/myDLL.cpp -I include -DMYDLL_EXPORTS # 用来定义头文件那里面的宏MYDLL_EXPORTS 此时为导出模式
  1. 编译main.cpp, 动态加载动态链接库,只有运行到需要动态链接库时候才会去对应目录中查找并加载
g++ ./src/main.cpp -o main.exe -I include
  1. 运行可执行程序
main.exe
# 输出:
# 3
# 100
# ImpMyDLL obj destroyed!
# hello dll !
posted @ 2023-03-20 19:12  raiuny  阅读(615)  评论(0编辑  收藏  举报