关于C++封装类为动态链接库

时间:2023-02-05 15:49:30

之前用caffe做图片分类,对caffe的代码进行了封装。为了让代码看起来尽可能简洁,对分类的类进行了封装,刚开始的封装是这样的:

classification.h如下:

#ifndef CLASSIFICATION_H_
#define CLASSIFICATION_H_

#include <caffe/caffe.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iosfwd>
#include <memory>
#include <utility>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
#include <time.h>

using namespace caffe;
using std::string;
typedef std::pair<int, float> Prediction;

class ClassifierImpl {
public:
ClassifierImpl(const string& model_file,
const string& trained_file,
const string& mean_file
);

std::vector<std::vector<Prediction> > Classify(const cv::Mat& img, int N = 2);
private:
void SetMean(const string& mean_file);

std::vector<std::vector<float> > Predict(const cv::Mat& img);

void WrapInputLayer(std::vector<cv::Mat>* input_channels);

void Preprocess(const cv::Mat& img,
std::vector<cv::Mat>* input_channels);

private:
shared_ptr<Net<float> > net_;
cv::Size input_geometry_;
int num_channels_;
cv::Mat mean_;
};
#endif

classification.cpp就是类函数的定义。


导出类的内容为:

由于导出类需要用到ClassifierImpl,那么导出类的成员变量就应该:

private:

ClassifierImpl *impl;

这样的话导出类头文件multi_recognition_gpu.h需要包含classification.h,否则ClassifierImpl 类型不明,但是这样使用dll的时候不是还需要classification.h了么?为了去掉classification.h,后来这样做:

导出类头文件multi_recognition_gpu.h

#ifndef MULTI_RECOGNITION_GPU_H_
#define MULTI_RECOGNITION_GPU_H_

#ifdef MULTI_RECOGNITION_API_EXPORTS
#define MULTI_RECOGNITION_API __declspec(dllexport)
#else
#define MULTI_RECOGNITION_API __declspec(dllimport)
#endif
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <io.h>
using std::string;
using std::vector;
typedef std::pair<int, float> Prediction;

class MULTI_RECOGNITION_API MultiClassifier
{
public:
MultiClassifier(const string& model_file,
const string& trained_file,
const string& mean_file);

~MultiClassifier();
std::vector<std::vector<Prediction> >Classify(const cv::Mat& img, int N = 2);
void getFiles(std::string path, std::vector<std::string>& files);
};

#endif


导出类cpp:

#include "multi_recognition_gpu.h"
#include "classification.h"
ClassifierImpl *impl=NULL;

MultiClassifier::MultiClassifier(const string& model_file, const string& trained_file, const string& mean_file)
{
Impl = new ClassifierImpl(model_file, trained_file, mean_file);
}
MultiClassifier::~MultiClassifier()
{
delete Impl;
}
std::vector<std::vector<Prediction> > MultiClassifier::Classify(const cv::Mat& img, int N /* = 2 */)
{
return Impl->Classify(img, N);
}

这样,使用dll的时候,就只需要一个头文件了。

但是,后来发现了很严重的问题。

使用dll时,只声明一个MultiClassifier对象,是没有问题的,但是声明两个或多个,就出问题了。问题就在全局的ClassifierImpl *impl=NULL,对象之间是共享的,new第一个对象时,impl指向了它,new第二个对像时,impl会改变指向,指向第二个对象,这样,第一个对象就丢失了,不但得到的结果不是我们所需的,并且内存泄漏(第一个对象没有释放)。

 

后来找到了解决办法:

导出类头文件multi_recognition_gpu.h修改为:

#ifndef MULTI_RECOGNITION_GPU_H_
#define MULTI_RECOGNITION_GPU_H_

#ifdef MULTI_RECOGNITION_API_EXPORTS
#define MULTI_RECOGNITION_API __declspec(dllexport)
#else
#define MULTI_RECOGNITION_API __declspec(dllimport)
#endif
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <io.h>
class ClassifierImpl;
using std::string;
using std::vector;
typedef std::pair<int, float> Prediction;

class MULTI_RECOGNITION_API MultiClassifier
{
public:
MultiClassifier(const string& model_file,
const string& trained_file,
const string& mean_file);

~MultiClassifier();
std::vector<std::vector<Prediction> >Classify(const cv::Mat& img, int N = 2);
void getFiles(std::string path, std::vector<std::string>& files);
private:
ClassifierImpl *Impl;
};

#endif
这里,不需要include classification.h,只需要加上class ClassifierImpl;告诉编译器ClassifierImpl是一个类就行。

然后导出类的cpp:

#include "multi_recognition_gpu.h"
#include "classification.h"

MultiClassifier::MultiClassifier(const string& model_file, const string& trained_file, const string& mean_file)
{
Impl = new ClassifierImpl(model_file, trained_file, mean_file);
}
MultiClassifier::~MultiClassifier()
{
delete Impl;
}
std::vector<std::vector<Prediction> > MultiClassifier::Classify(const cv::Mat& img, int N /* = 2 */)
{
return Impl->Classify(img, N);
}

就不需要全局变量了。


得到的教训是,尽量不要使用全局变量!!!!!!!!!!!!!!!!!