DLL导出函数Interface与实现分离
时间:2022-07-23
本文章向大家介绍DLL导出函数Interface与实现分离,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一种最简单的实现
GPImpl.h/.cpp
class GPImpl
{
public:
void DoSomeThing();
};
//------------------------------
void GPImpl::DoSomeThing()
{
cout << "DoSomeThing" << endl;
}
GPExp.h/.cpp
#include "GPImpl.h"
class GP_EXPORT IGPExp
{
public:
void DoSomeThing();
private:
GPImpl m_GPImpl;
};
//----------------------------------
void IGPExp::DoSomeThing()
{
m_GPImpl.DoSomeThing();
}
这时如果我们提供了GPExp.h,那么m_GPImpl也暴露了,这时我们必须同时提供GPImpl.h,如果GPExp.h中有很多m_GPImpl这样的成员变量,那我们就得提供很多头文件了,而且只要其中任一个类有变动,我们都要给用户更新头文件,
也就是需要重新编译,对于大项目来说,这是要命的
//--------------------------------------------------------改进:
接口与实现分离
对IGPExp这个导出类写一个实现类GPExpImpl来实现它的所有功能,重点:GPExpImpl必须和IGPExp有相同的公有成员函数,因为它们一个是接口,一个是实现,要一一对应
GPImpl.h/.cpp
class GPImpl
{
public:
void DoSomeThing();
};
/// <summary>
/// GPExp的实现类
/// </summary>
class GPExpImpl
{
public:
void DoSomeThing();
private:
GPImpl m_GPImpl;
};
//----------------------------------------------------
void GPImpl::DoSomeThing()
{
cout << "DoSomeThing" << endl;
}
//====
void GPExpImpl::DoSomeThing()
{
m_GPImpl.DoSomeThing();
}
GPExp.h/.cpp
#pragma once
class GPExpImpl;// 这样声明就不需要包含头文件
class GP_EXPORT IGPExp
{
public:
IGPExp();
~IGPExp();
void DoSomeThing();
private:
GPExpImpl* m_pImpl;
};
//----------------------------------------------------------
#include "GPExp.h"
#include "incGPImpl.h"
IGPExp::IGPExp()
{
m_pImpl = new GPExpImpl;
}
IGPExp::~IGPExp()
{
if (m_pImpl)
{
delete m_pImpl;
}
}
void IGPExp::DoSomeThing()
{
m_pImpl->DoSomeThing();
}
划重点:前置声明class GPExpImpl;不需要包含头文件,但GPExpImpl只能使用指针,否则过不了编译
//-----------------------------------------深入,如果IGPExp有父类,父类有函数IsOk来控制是否调用DoSomeThing()
第一种方式:IGPExp::DoSomeThing()判断
GPExp.h/.cpp
class GPExpImpl;
class GP_EXPORT IGPExpBase
{
public:
bool IsOk();
virtual void DoSomeThing() = 0;
};
class GP_EXPORT IGPExp : public IGPExpBase
{
public:
IGPExp();
~IGPExp();
void DoSomeThing();
private:
GPExpImpl* m_pImpl;
};
//----------------------------------------------------
#include "GPExp.h"
#include "incGPImpl.h"
bool IGPExpBase::IsOk()
{
return true;
}
//-------------------------------
IGPExp::IGPExp()
{
m_pImpl = new GPExpImpl;
}
IGPExp::~IGPExp()
{
if (m_pImpl)
{
delete m_pImpl;
}
}
void IGPExp::DoSomeThing()
{
if (IsOk())
{
m_pImpl->DoSomeThing();
}
}
这样的话,接口就加入了实现细节,从而接口和实现没有彻底分离
更好的方式,把IGPExp的指针传给实现GPExpImpl
GPImpl.h/.cpp
#pragma once
class IGPExp;
class GPImpl
{
public:
void DoSomeThing();
};
/// <summary>
/// GPExp的实现类
/// </summary>
class GPExpImpl
{
public:
GPExpImpl(IGPExp* pExp);
void DoSomeThing();
private:
GPImpl m_GPImpl;
IGPExp* m_pExp;
};
//-----------------------------------------
#include "GPImpl.h"
#include "..\GPExp.h"
#include <iostream>
using namespace std;
void GPImpl::DoSomeThing()
{
cout << "DoSomeThing" << endl;
}
//====
GPExpImpl::GPExpImpl(IGPExp* pExp)
{
m_pExp = pExp;
}
void GPExpImpl::DoSomeThing()
{
if (m_pExp->IsOk())
{
m_GPImpl.DoSomeThing();
}
}
GPExp.h/.cpp
class GPExpImpl;
class GP_EXPORT IGPExpBase
{
public:
bool IsOk();
virtual void DoSomeThing() = 0;
};
class GP_EXPORT IGPExp : public IGPExpBase
{
public:
IGPExp();
~IGPExp();
void DoSomeThing();
private:
GPExpImpl* m_pImpl;
};
//--------------------------------------------
#include "GPExp.h"
#include "incGPImpl.h"
bool IGPExpBase::IsOk()
{
return true;
}
//-------------------------------
IGPExp::IGPExp()
{
m_pImpl = new GPExpImpl(this);
}
IGPExp::~IGPExp()
{
if (m_pImpl)
{
delete m_pImpl;
}
}
void IGPExp::DoSomeThing()
{
m_pImpl->DoSomeThing();
}
为什么不让GPExpImpl直接继承IGPExpBase呢,因为GPExpImpl定位为IGPExp的执行体,和IGPExpBase实在没有什么关系
- 18.9/18.10 LVS NAT模式搭建
- 谈谈WCF中的Data Contract (1):Data Contract Overview
- Linux基础(day66)
- 字符串的驻留(String Interning)
- 19.5 忘记Admin密码如何做
- 19.3/19.4/19.6 安装zabbix
- 欲火焚身,心静则凉--只靠冲动是不能长久地
- 深入理解C#3.x的新特性(4):Automatically Implemented Property
- 增加网卡注意点
- 深入理解C# 3.x的新特性(1): Anonymous Type
- Linux基础(day65)
- 18.12 keepalived + LVS
- 学会JS只是知道了“是什么”,并不意味着你会用JS做什么
- Linux基础(day68)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python中的多处理与多线程:新手简介
- Fortran中的陷阱-NAMELIST
- 当Excel遇到大数据问题,是时候用Python来拯救了
- PySCF程序包平均场计算的一些收敛技巧
- 你应该知道的10个Python文件系统方法
- 适合初学者的Python装饰器的简易教程
- 一起刷Leetcode第一篇,数组和字典的妙用
- 加速Python列表和字典,让你代码更加高效
- 如何使用Python的Flask和谷歌app Engine来构建一个web app
- 如何用Python实现电子邮件的自动化
- 在Win下安装Visual Studio和Parallel Studio XE
- 我们将项目语言从Python转向Go的5个原因
- GFN-xTB的编译与API使用
- 红外光谱的理论计算
- 一起刷题(leetcode)第二篇:如何用Python实现递归