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

前一篇文章中讲过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

DLLs in Visual C++

Microsoft Visual Studio .NET 2003 Warning C4251

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

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值