本文主要介绍在Linux环境下C++如何生成库文件,如何使用库文件,将以几个例子进行说明库开发人员角度和应用开发人员(使用库文件人员)角度进行说明。
- C++静态库
- C++多个文件的静态库
- C++动态库
1.静态库
编译时静态库必须存在
静态库会增加编译后的可执行文件大小
1.1.使用单个文件组成库
1.1.1.目录结构
staticlibtest1r
├── libs
│ ├── headers
│ │ └── increment.hpp
│ ├── increment.cpp
└── src
├── headers
├── libs
└── test_increment.cpp
1.1.2.编译并使用库
- 编译生成库文件
cd libs
# statictest1/libs目录下
# -c 代表只编译不链接
g++ -c increment.cpp -o increment.o
# 生成静态库文件
ar rcs libincrement.a increment.o
会在statictest1/libs下生成increment.o
和libincrement.a
,后者就是我们要的库文件
2. 拷贝库文件和头文件
将其拷贝到statictest1/src/libs下
cd ..
# statictest1目录下
# 拷贝
cp libs/libincrement.a src/libs/
cp libs/headers/increment.hpp src/headers/
- 编译运行测试
cd src
# statictest1/src目录下
# 编译可执行文件
g++ test_increment.cpp -L./libs -lincrement -o test_increment
# 执行
./test_increment
# 运行结果
Before incrementing: a b c d e
After incrementing: b c d e f
1.1.3.文件内容
increment.hpp
文件内容
#ifndef INCREMENT_H
#define INCREMENT_H
#include <vector>
class CharIncrement {
public:
static void incrementChars(std::vector<char>& chars);
};
#endif // INCREMENT_H
increment.cpp
文件内容
#include "./headers/increment.hpp"
void CharIncrement::incrementChars(std::vector<char>& chars) {
for (auto& c : chars) {
c = c + 1;
}
}
test_increment.cpp
文件内容
#include <iostream>
#include <vector>
#include "./headers/increment.hpp"
int main() {
std::vector<char> chars = {'a', 'b', 'c', 'd', 'e'};
std::cout << "Before incrementing: ";
for (const auto& c : chars) {
std::cout << c << " ";
}
std::cout << std::endl;
CharIncrement::incrementChars(chars);
std::cout << "After incrementing: ";
for (const auto& c : chars) {
std::cout << c << " ";
}
std::cout << std::endl;
return 0;
}
1.2.使用多个文件组成的库
1.2.1.目录结构
staticlibtest2
├── cppsrc
│ ├── pics
│ │ └── input_image.jpg
│ └── src
│ ├── headers
│ ├── libs
│ └── test_image.cpp
└── libsrc
├── headers
│ ├── image_loader.hpp
│ ├── image_processor.hpp
│ └── image_saver.hpp
├── libs
├── image_loader.cpp
├── image_processor.cpp
└── image_saver.cpp
1.2.2.编译并使用库
- 编译生成库文件
cd libsrc
# statictest2/libsrc目录下
g++ -c image_loader.cpp -o image_loader.o `pkg-config --cflags opencv4`
g++ -c image_processor.cpp -o image_processor.o `pkg-config --cflags opencv4`
g++ -c image_saver.cpp -o image_saver.o `pkg-config --cflags opencv4`
# 生成静态库文件
ar rcs ./libs/libimage_processing.a image_loader.o image_processor.o image_saver.o
# 上述四行也可以简化成以下一行
# ar rcs ./libs/libimage_processing.a image_loader.cpp image_processor.cpp image_saver.cpp `pkg-config --cflags opencv4`
会在statictest2/libsrc下生成image_loader.o
,image_processor.o
,image_saver.o
三个目标文件,并在statictest2/libsrc/libs下生成一个库文件libimage_processing.a
2. 拷贝库文件和头文件
cd ..
# statictest2目录下
cp libsrc/libs/libimage_processing.a cppsrc/src/libs/
cp libsrc/headers/* cppsrc/src/headers/
- 编译运行测试
cd cppsrc/src
# statictest2/cppsrc/src目录下
# 编译可执行文件
g++ test_image.cpp -L./libs -limage_processing `pkg-config --cflags --libs opencv4` -o test_image
# 执行
./test_image
# 运行结果
Image loaded successfully. Converting to grayscale...
Grayscale image saved successfully.
Applying Gaussian blur...
Blurred image saved successfully.
运行后的图片
1.2.3.文件内容
image_loader.cpp
文件内容
#include "./headers/image_loader.hpp"
#include <fstream>
#include <stdexcept>
ImageLoader::ImageLoader(const std::string& filename){
Image = load(filename);
Filename = filename;
}
ImageLoader::ImageLoader(const std::string& filename,const int width,const int height){
Image = load(filename);
Filename = filename;
Width = width;
Height = height;
}
cv::Mat ImageLoader::load(const std::string& filename) {
cv::Mat image = cv::imread(filename, cv::IMREAD_COLOR);
if (image.empty()) {
throw std::runtime_error("Failed to load image");
}
return image;
}
image_loader.hpp
文件内容
#ifndef IMAGE_LOADER_H
#define IMAGE_LOADER_H
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>
class ImageLoader {
public:
ImageLoader() {
}
ImageLoader(const std::string& filename);
ImageLoader(const std::string& filename,const int width,const int height);
cv::Mat load(const std::string& filename);
cv::Mat Image;
std::string Filename;
int Width;
int Height;
};
#endif // IMAGE_LOADER_H
image_processor.cpp
文件内容
#include "./headers/image_processor.hpp"
#include <opencv2/opencv.hpp>
void ImageProcessor::convertToGrayscale(cv::Mat& image) {
cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);
}
void ImageProcessor::applyGaussianBlur(cv::Mat& image, int kernelSize, double sigma) {
cv::GaussianBlur(image, image, cv::Size(kernelSize, kernelSize), sigma);
}
image_processor.hpp
文件内容
#ifndef IMAGE_PROCESSOR_H
#define IMAGE_PROCESSOR_H
#include <opencv2/opencv.hpp>
class ImageProcessor {
public:
static void convertToGrayscale(cv::Mat& image);
static void applyGaussianBlur(cv::Mat& image, int kernelSize, double sigma);
};
#endif // IMAGE_PROCESSOR_H
image_saver.cpp
文件内容
#include "./headers/image_saver.hpp"
#include <opencv2/opencv.hpp>
void ImageSaver::save(const cv::Mat& image, const std::string& filename) {
if (!cv::imwrite(filename, image)) {
throw std::runtime_error("Failed to save image");
}
}
image_saver.hpp
文件内容
#ifndef IMAGE_SAVER_H
#define IMAGE_SAVER_H
#include <opencv2/opencv.hpp>
class ImageSaver {
public:
static void save(const cv::Mat& image, const std::string& filename);
};
#endif // IMAGE_SAVER_H
test_image.cpp
文件内容
#include <iostream>
#include <string>
#include "./headers/image_loader.hpp"
#include "./headers/image_processor.hpp"
#include "./headers/image_saver.hpp"
int main() {
try {
std::string inputFilename = "../pics/input_image.jpg";
std::string outputFilename1 = "../pics/output_grayscale.jpg";
std::string outputFilename2 = "../pics/output_blur.jpg";
ImageLoader il = ImageLoader(inputFilename);
cv::Mat image = il.load(inputFilename);
cv::Mat image2 = image.clone();
std::cout << "Image loaded successfully. Converting to grayscale..." << std::endl;
ImageProcessor::convertToGrayscale(image);
ImageSaver::save(image, outputFilename1);
std::cout << "Grayscale image saved successfully." << std::endl;
std::cout << "Applying Gaussian blur..." << std::endl;
ImageProcessor::applyGaussianBlur(image2, 11, 5.0);
ImageSaver::save(image2, outputFilename2);
std::cout << "Blurred image saved successfully." << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
2.动态库
运行时动态库必须存在
动态库使用动态链接,相比静态库可让可执行文件变小
2.1.目录结构
alibtest2
├── app
│ ├── pics
│ │ └── input_image.jpg
│ └── src
│ ├── headers
│ ├── libs
│ └── test_image.cpp
└── lib
├── libs
└── src
├── headers
│ ├── image_loader.hpp
│ ├── image_processor.hpp
│ └── image_saver.hpp
├── image_loader.cpp
├── image_processor.cpp
└── image_saver.cpp
2.2.编译并使用库
- 编译生成库
cd lib/src
# alibtest2/lib/src下
g++ -fPIC -c image_loader.cpp -o image_loader.o `pkg-config --cflags opencv4`
g++ -fPIC -c image_processor.cpp -o image_processor.o `pkg-config --cflags opencv4`
g++ -fPIC -c image_saver.cpp -o image_saver.o `pkg-config --cflags opencv4`
# 编译
g++ -shared -fPIC -o ../libs/libimage_processing.so image_loader.o image_processor.o image_saver.o
# -fPIC:
# 生成位置无关代码(Position Independent Code)。这对于创建共享库很重要,因为它允许库代码在内存中的任何位置加载和执行。
- 拷贝头文件和动态库
cp headers/* ../../app/src/headers
cp ../libs/libimage_processing.so ../../app/src/libs/
- 更新动态库加载路径
将库文件路径添加进动态库搜索路径,即在/etc/ld.so.conf.d/
目录下添加me.conf
文件,
me.conf
内容:/home/gh/workspace/libtest/alibtest1_app/src/libs
添加成功后使用ldconfig
重新加载
echo "/home/gh/workspace/libtest/alibtest2/lib/libs" >> /etc/ld.so.conf.d/me.conf
ldconfig
若少了这一步,强制编译出的运行文件还无法动态连接到库
- 编译运行测试
cd ../../app/src
# alibtest2/app/src下
# 编译
g++ test_image.cpp -L./libs -limage_processing `pkg-config --cflags --libs opencv4` -o test_image
# 查看库文件映射情况
# ldd test_image
# 运行
./test_image
# 运行结果
root@gh:/home/gh/workspace/libtest/alibtest2/app/src# ./test_image
Image loaded successfully. Resizing width...
Resize width image saved successfully.
Resizing height...
Resize height image saved successfully.
在app/src/pics目录下生成两个输出图像output_resizewidth.jpg
和output_resizeheight.jpg
5. 运行结果
原图与输出图像
2.3.文件内容
test_image.cpp
文件内容
#include <iostream>
#include <string>
#include "./headers/image_loader.hpp"
#include "./headers/image_processor.hpp"
#include "./headers/image_saver.hpp"
int main() {
try {
std::string inputFilename = "../pics/input_image.jpg";
std::string outputFilename1 = "../pics/output_resizewidth.jpg";
std::string outputFilename2 = "../pics/output_resizeheight.jpg";
ImageProcessor imagep;
ImageLoader il = ImageLoader(inputFilename);
cv::Mat image = il.load(inputFilename);
cv::Mat image2 = image.clone();
std::cout << "Image loaded successfully. Resizing width..." << std::endl;
imagep.resizeWidth(image);
ImageSaver::save(image, outputFilename1);
std::cout << "Resize width image saved successfully." << std::endl;
std::cout << "Resizing height..." << std::endl;
imagep.resizeHeight(image2);
ImageSaver::save(image2, outputFilename2);
std::cout << "Resize height image saved successfully." << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
image_loader.cpp
文件内容
#include "./headers/image_loader.hpp"
#include <fstream>
#include <stdexcept>
ImageLoader::ImageLoader(const std::string& filename){
Image = load(filename);
Filename = filename;
}
ImageLoader::ImageLoader(const std::string& filename,const int width,const int height){
Image = load(filename);
Filename = filename;
Width = width;
Height = height;
}
cv::Mat ImageLoader::load(const std::string& filename) {
cv::Mat image = cv::imread(filename, cv::IMREAD_COLOR);
if (image.empty()) {
throw std::runtime_error("Failed to load image");
}
return image;
}
image_loader.hpp
文件内容
#ifndef IMAGE_LOADER_H
#define IMAGE_LOADER_H
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>
class ImageLoader {
public:
ImageLoader() {
}
ImageLoader(const std::string& filename);
ImageLoader(const std::string& filename,const int width,const int height);
cv::Mat load(const std::string& filename);
cv::Mat Image;
std::string Filename;
int Width;
int Height;
};
#endif // IMAGE_LOADER_H
image_processor.cpp
文件内容
#include "./headers/image_processor.hpp"
#include <opencv2/imgproc.hpp>
void ImageProcessor::resizeWidth(cv::Mat& image) {
int newWidth = image.cols / 2;
cv::resize(image, image, cv::Size(newWidth, image.rows));
}
void ImageProcessor::resizeHeight(cv::Mat& image) {
int newHeight = image.rows / 2;
cv::resize(image, image, cv::Size(image.cols, newHeight));
}
image_processor.hpp
文件内容
#ifndef IMAGE_PROCESSOR_H
#define IMAGE_PROCESSOR_H
#include <opencv2/opencv.hpp>
class ImageProcessor {
public:
void resizeWidth(cv::Mat& image);
void resizeHeight(cv::Mat& image);
};
#endif // IMAGE_PROCESSOR_H
image_saver.cpp
文件内容
#include "./headers/image_saver.hpp"
#include <opencv2/opencv.hpp>
void ImageSaver::save(const cv::Mat& image, const std::string& filename) {
if (!cv::imwrite(filename, image)) {
throw std::runtime_error("Failed to save image");
}
}
image_saver.hpp
文件内容
#ifndef IMAGE_SAVER_H
#define IMAGE_SAVER_H
#include <opencv2/opencv.hpp>
class ImageSaver {
public:
static void save(const cv::Mat& image, const std::string& filename);
};
#endif // IMAGE_SAVER_H