前一篇文章中讲过C++中如何正确的定义接口类,那定义好的接口类如何正确使用?本篇将细细说说。
提供接口与实现
首先,声明一个接口:
-
// circle.h
-
// 圆的接口类
-
class Circle {
-
public:
-
virtual ~Circle() {};
-
-
// 接口方法:面积
-
virtual double area() =
0;
-
};
通过继承的方式实现这个接口:
-
// circle_impl.h
-
#include "circle.h"
-
-
// 圆的具体实现类
-
class CircleImpl :
public Circle {
-
-
private:
-
double radius;
-
public:
-
CircleImpl(
double radius);
-
double area() override;
-
};
-
// circle_impl.cpp
-
#include <cmath>
-
#include "circle_impl.h"
-
-
inline double pi() {
-
return
std::
atan(
1) *
4;
-
};
-
-
CircleImpl::CircleImpl(
double _radius) : radius(_radius) {
-
};
-
-
double CircleImpl::area() {
-
return pi() * radius * radius;
-
};
最后,通过管理类创建接口派生类的实例,或者销毁接口派生类的实例:
-
// circle_manager.h
-
#include "circle.h"
-
-
// 圆的创建工厂类
-
class CircleManager {
-
public:
-
static Circle* create(double radius);
// 创建circle实例
-
static void destroy(Circle* circlePtr);
// 销毁circle实例
-
};
-
// circle_manager.cpp
-
#include "circle_manager.h"
-
#include "circle_impl.h"
-
-
Circle* CircleManager::create(double radius) {
-
Circle* circlePtr =
new CircleImpl(radius);
-
-
return circlePtr;
-
};
-
-
void CircleManager::destroy(Circle* circlePtr) {
-
delete circlePtr;
-
};
代码目录结构:
proj-+ |-inc-+ | |-circle.h | |-circle_manager.h | |-src-+ |-circle_impl.h |-circle_impl.cpp |-circle_manager.cpp
其中inc目录用于存放Circle接口类和Circle管理类的声明,src目录中存放Circle实现类CircleImpl的声明和定义、Circle管理类CircleManager的定义。
然后,可以将以上代码编译成静态库circle.lib,并和inc目录中的头文件一起提供给外部调用:
如何使用静态库?
外部使用者编译时,需要做如下配置:
1). 把inc目录添加到“附加包含目录”中。
2). “附加依赖项”中添加circle.lib。
3). 把circle.lib所在目录的路径添加到“附加库目录”中。
外部使用者的代码如下:
-
// main.cpp
-
#include <iostream>
-
#include "circle_manager.h"
-
#include "circle.h"
-
-
int main()
-
{
-
Circle* circlePtr = CircleManager::create(
3);
-
cout << circlePtr->area() <<
endl;
-
CircleManager::destroy(circlePtr);
-
-
system(
"pause");
-
-
return
0;
-
}
以上代码只提供给外部circle的接口,circle的实现完全被隐藏了起来,外部将无从知晓,外部使用者只能通过circle管理类生成circle的派生类的实例。外部使用者得到circle派生类的实例后,除了能调用接口暴露的方法area()外,其它什么也做不了,这样就完全达到了使用接口的最终目标。
如何编译成动态库?
首先,添加一个新的头文件:
-
// dll_export.h
-
// if windows .dll
-
#ifdef _WINDLL
-
-
#ifdef DLL_API_EXPORTS
-
#define DLL_API __declspec(dllexport)
-
#else
-
#define DLL_API __declspec(dllimport)
-
#endif
-
-
// else if Linux or macOS .so
-
#else
-
#define DLL_API
-
#endif
添加此头文件后,代码可以在windows、Linux下都可编译生成动态库,只需在编译时设置不同参数就行了。
windows: /D "DLL_API_EXPORTS" /D "_WINDLL"
Linux: 不用配置额外参数
circle.h和circle_manager.h也要做相应改动:
-
// circle.h
-
#pragma once
-
#include "dll_export.h"
-
-
// 圆的接口类
-
class DLL_API Circle
-
{
-
public:
-
virtual ~Circle() {};
-
-
// 接口方法:面积
-
virtual double area() =
0;
-
};
-
// circle_manager.h
-
#pragma once
-
-
#include "circle.h"
-
#include "dll_export.h"
-
-
// 圆的创建工厂类
-
class DLL_API CircleManager
-
{
-
public:
-
static Circle* create(double radius);
-
static void destroy(Circle* circlePtr);
-
};
编译完成后将生成”circle.lib“和”circle.dll“文件:
proj-+ |-inc-+ | |-circle.h | |-circle_manager.h | |-src-+ | |-circle_impl.h | |-circle_impl.cpp | |-circle_manager.cpp | |-bin-+ |-circle.lib |-circlr.dll
如何使用动态库?
外部使用者编译时,需要做如下配置:
1). 代码中添加#pragma comment(lib,"circle.lib"), 这里是circle.lib,不是circle.dll。
2). 把inc目录添加到“附加包含目录”中。
3). “附加依赖项”中添加circle.lib,这里也是circle.lib,不是circle.dll。
4). 把bin目录所在路径添加到”附加库目录“中。
新的外部使用者的代码如下:
-
#include <iostream>
-
-
#include "circle_manager.h"
-
#include "circle.h"
-
-
#pragma comment(lib,"circle.lib")
-
-
int main()
-
{
-
Circle* circlePtr = CircleManager::create(
3);
-
cout << circlePtr->area() <<
endl;
-
CircleManager::destroy(circlePtr);
-
-
system(
"pause");
-
-
return
0;
-
}
总结
这里有几点需要说明一下:
1、为什么CircleManager类即在提供创建实例的方法又要提供销毁实例的方法?
由于编译器的实现方式不同,dll的堆空间可能跟调用方的堆空间不同,它可能是由dll自己单独管理的,所以从dll中创建的实例,最好还是在dll中销毁。
2、对动态库的调用本文是通过隐式调用的方式完成的,对动态库的调用也可以使用显式调用的方式,但由于windows和Linux在使用显式调用时的API是不同的,不好提供统一的代码,所以本文没有举例,以后有机会再单独行文介绍。
参考文档
HowTo: Export C++ classes from a DLL
Exporting C++ classes from a DLL
Microsoft Visual Studio .NET 2003 Warning C4251
Exporting classes containing std:: objects (vector, map, etc) from a dll
本文详细介绍了在C++中如何定义并使用接口类,包括接口类的声明、实现,以及通过管理类创建和销毁实例的过程。同时,探讨了如何将此类代码封装为静态库和动态库供外部调用,提供了具体的代码示例。
2087

被折叠的 条评论
为什么被折叠?



