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 导出静态链接库并使用
- 编译mylib.cpp成.o文件
g++ -c ./src/mylib.cpp -o ./out/mylib.o -I include
- 将.o文件打包成静态链接库.a
使用ar命令(archive) rcs (replace create symbolize)
ar rcs ./lib/libmylib.a ./out/mylib.o
- 编译main.cpp, 需要包含刚刚生成的静态链接库以及头文件,不需要mylib.cpp文件
g++ ./src/main.cpp -o main -I include -L lib -l mylib
- 运行可执行程序
./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 导出静态链接库并使用
- 编译mylib.cpp成.o文件
g++ -c ./src/mylib.cpp -o ./out/mylib.o -I include
- 将.o文件打包成静态链接库.lib
使用ar命令(archive) rcs (replace create symbolize)
ar rcs ./lib/mylib.lib ./out/mylib.o
- 编译main.cpp, 需要包含刚刚生成的静态链接库以及头文件,不需要mylib.cpp文件
g++ ./src/main.cpp -o main.exe -I include -L lib -l mylib
- 运行可执行程序
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 导出动态链接库并使用
- 导出so动态链接文件
g++ -shared -fpic -o ./dll/myDLL.so ./src/myDLL.cpp -I include
- 编译main.cpp, 动态加载动态链接库,只有运行到需要动态链接库时候才会去对应目录中查找并加载
g++ ./src/main.cpp -o main -I include
- 运行可执行程序
./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 导出动态链接库并使用
- 导出dll动态链接文件
g++ -shared -fpic -o ./dll/myDLL.dll ./src/myDLL.cpp -I include -DMYDLL_EXPORTS # 用来定义头文件那里面的宏MYDLL_EXPORTS 此时为导出模式
- 编译main.cpp, 动态加载动态链接库,只有运行到需要动态链接库时候才会去对应目录中查找并加载
g++ ./src/main.cpp -o main.exe -I include
- 运行可执行程序
main.exe
# 输出:
# 3
# 100
# ImpMyDLL obj destroyed!
# hello dll !