C++:如何正确的使用接口类

本文详细介绍了在C++中如何定义并使用接口类,包括接口类的声明、实现,以及通过管理类创建和销毁实例的过程。同时,探讨了如何将此类代码封装为静态库和动态库供外部调用,提供了具体的代码示例。

前一篇文章中讲过C++中如何正确的定义接口类,那定义好的接口类如何正确使用?本篇将细细说说。

提供接口与实现

首先,声明一个接口:


 
  1. // circle.h
  2. // 圆的接口类
  3. class Circle {
  4. public:
  5. virtual ~Circle() {};
  6. // 接口方法:面积
  7. virtual double area() = 0;
  8. };

通过继承的方式实现这个接口:


 
  1. // circle_impl.h
  2. #include "circle.h"
  3. // 圆的具体实现类
  4. class CircleImpl : public Circle {
  5. private:
  6. double radius;
  7. public:
  8. CircleImpl( double radius);
  9. double area() override;
  10. };

 
  1. // circle_impl.cpp
  2. #include <cmath>
  3. #include "circle_impl.h"
  4. inline double pi() {
  5. return std:: atan( 1) * 4;
  6. };
  7. CircleImpl::CircleImpl( double _radius) : radius(_radius) {
  8. };
  9. double CircleImpl::area() {
  10. return pi() * radius * radius;
  11. };

最后,通过管理类创建接口派生类的实例,或者销毁接口派生类的实例:


 
  1. // circle_manager.h
  2. #include "circle.h"
  3. // 圆的创建工厂类
  4. class CircleManager {
  5. public:
  6.      static Circle* create(double radius); // 创建circle实例
  7.      static void destroy(Circle* circlePtr); // 销毁circle实例
  8. };

 
  1. // circle_manager.cpp
  2. #include "circle_manager.h"
  3. #include "circle_impl.h"
  4. Circle* CircleManager::create(double radius) {
  5.     Circle* circlePtr = new CircleImpl(radius);
  6.      return circlePtr;
  7. };
  8. void CircleManager::destroy(Circle* circlePtr) {
  9.      delete circlePtr;
  10. }; 

代码目录结构:


 
  1. proj-+
  2. |-inc-+
  3. | |-circle.h
  4. | |-circle_manager.h
  5. |
  6. |-src-+
  7. |-circle_impl.h
  8. |-circle_impl.cpp
  9. |-circle_manager.cpp

其中inc目录用于存放Circle接口类和Circle管理类的声明,src目录中存放Circle实现类CircleImpl的声明和定义、Circle管理类CircleManager的定义。

然后,可以将以上代码编译成静态库circle.lib,并和inc目录中的头文件一起提供给外部调用:

如何使用静态库?

外部使用者编译时,需要做如下配置:

1). 把inc目录添加到“附加包含目录”中。

2). “附加依赖项”中添加circle.lib。

3). 把circle.lib所在目录的路径添加到“附加库目录”中。

外部使用者的代码如下:


 
  1. // main.cpp
  2. #include <iostream>
  3. #include "circle_manager.h"
  4. #include "circle.h"
  5. int main()
  6. {
  7. Circle* circlePtr = CircleManager::create( 3);
  8. cout << circlePtr->area() << endl;
  9. CircleManager::destroy(circlePtr);
  10. system( "pause");
  11. return 0;
  12. }

以上代码只提供给外部circle的接口,circle的实现完全被隐藏了起来,外部将无从知晓,外部使用者只能通过circle管理类生成circle的派生类的实例。外部使用者得到circle派生类的实例后,除了能调用接口暴露的方法area()外,其它什么也做不了,这样就完全达到了使用接口的最终目标。

如何编译成动态库?

首先,添加一个新的头文件:


 
  1. // dll_export.h
  2. // if windows .dll
  3. #ifdef _WINDLL
  4. #ifdef DLL_API_EXPORTS
  5. #define DLL_API __declspec(dllexport)
  6. #else
  7. #define DLL_API __declspec(dllimport)
  8. #endif
  9. // else if Linux or macOS .so
  10. #else
  11. #define DLL_API
  12. #endif

添加此头文件后,代码可以在windows、Linux下都可编译生成动态库,只需在编译时设置不同参数就行了。

windows: /D "DLL_API_EXPORTS" /D "_WINDLL"

Linux: 不用配置额外参数

circle.h和circle_manager.h也要做相应改动:


 
  1. // circle.h
  2. #pragma once
  3. #include "dll_export.h"
  4. // 圆的接口类
  5. class DLL_API Circle
  6. {
  7. public:
  8. virtual ~Circle() {};
  9. // 接口方法:面积
  10. virtual double area() = 0;
  11. };

 
  1. // circle_manager.h
  2. #pragma once
  3. #include "circle.h"
  4. #include "dll_export.h"
  5. // 圆的创建工厂类
  6. class DLL_API CircleManager
  7. {
  8. public:
  9. static Circle* create(double radius);
  10. static void destroy(Circle* circlePtr);
  11. };

编译完成后将生成”circle.lib“和”circle.dll“文件:


 
  1. proj-+
  2. |-inc-+
  3. | |-circle.h
  4. | |-circle_manager.h
  5. |
  6. |-src-+
  7. | |-circle_impl.h
  8. | |-circle_impl.cpp
  9. | |-circle_manager.cpp
  10. |
  11. |-bin-+
  12. |-circle.lib
  13. |-circlr.dll

如何使用动态库?

外部使用者编译时,需要做如下配置:

1). 代码中添加#pragma comment(lib,"circle.lib"), 这里是circle.lib,不是circle.dll。

2). 把inc目录添加到“附加包含目录”中。

3). “附加依赖项”中添加circle.lib,这里也是circle.lib,不是circle.dll。

4). 把bin目录所在路径添加到”附加库目录“中。

 

新的外部使用者的代码如下:


 
  1. #include <iostream>
  2. #include "circle_manager.h"
  3. #include "circle.h"
  4. #pragma comment(lib,"circle.lib")
  5. int main()
  6. {
  7. Circle* circlePtr = CircleManager::create( 3);
  8. cout << circlePtr->area() << endl;
  9. CircleManager::destroy(circlePtr);
  10. system( "pause");
  11. return 0;
  12. }

总结

这里有几点需要说明一下:

1、为什么CircleManager类即在提供创建实例的方法又要提供销毁实例的方法?

由于编译器的实现方式不同,dll的堆空间可能跟调用方的堆空间不同,它可能是由dll自己单独管理的,所以从dll中创建的实例,最好还是在dll中销毁。

2、对动态库的调用本文是通过隐式调用的方式完成的,对动态库的调用也可以使用显式调用的方式,但由于windows和Linux在使用显式调用时的API是不同的,不好提供统一的代码,所以本文没有举例,以后有机会再单独行文介绍。

 

参考文档

HowTo: Export C++ classes from a DLL

Exporting C++ classes from a DLL

DLLs in Visual C++

Microsoft Visual Studio .NET 2003 Warning C4251

Exporting classes containing std:: objects (vector, map, etc) from a dll

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值