C++ 设计技巧 Pimpl模式

2023-11-13

什么是Pimpl?

Pimpl(Pointer to implementation) 是一种隐藏实现的小技巧。尤其是我们在对外提供API时,通常需要将头文件给出,这样很明显的将头文件中的私有成员暴漏给外部。另外Pimpl还可以少代码依赖和编译时间,实现了简单的编译解耦。

Pimpl如何实现?

在接口类内部实例化一个业务的子类,将函数功能实现写入到业务的子类中,接口类本身的函数只用于和外部的业务交互。
通过Pimpl又达成了接口与实现的解耦,提高了代码的可复用性(移植性)。

代码实现

  • 首选创建好实现类
//=================================traffic.h=================================//
#pragma once
#include <string>
class Traffic {
 public:
  Traffic();
  ~Traffic();
  int GetType();
  int GetPrice();
  std::string GetName();

 private:
  int type_;
  int price_;
  std::string name_;
};
//=================================traffic.cpp=================================//
#include "traffic.h"

Traffic::Traffic() {
  type_=1;
  price_=1000;
  name_ = "xiaomi car";
}

Traffic::~Traffic() {}

int Traffic::GetType() { return type_; }

int Traffic::GetPrice() { return price_; }

std::string Traffic::GetName() { return name_; }

  • 接着实现我们的接口类,通过接口函数来调用与之对应的实例类函数
//=================================i_traffic.h=================================//
#pragma once
#include <iostream>
#include <memory>
class Traffic;
class I_Traffic {
 public:
  I_Traffic();
  ~I_Traffic();
  int GetType();
  int GetPrice();
  std::string GetName();
 private:
  std::unique_ptr<Traffic> traffic_pimpl_;
};
//=================================i_traffic.cpp=================================//
#include "i_traffic.h"
#include "traffic.h"
I_Traffic::I_Traffic() : 
	traffic_pimpl_(std::make_unique<Traffic>()) {}
I_Traffic::~I_Traffic() {
}

int I_Traffic::GetType() { 
	return traffic_pimpl_->GetType(); 
}
int I_Traffic::GetPrice() { 
	return traffic_pimpl_->GetPrice();
}
std::string I_Traffic::GetName() { 
	return traffic_pimpl_->GetName(); 
}
  • 调用
#include <iostream>
#include <memory>
#include "i_traffic.h"
using namespace std;
int main() {
  std::unique_ptr<I_Traffic> traffic = std::make_unique<I_Traffic>();
  cout << traffic->GetName() << endl;
  cout << traffic->GetPrice() << endl;
  cout << traffic->GetType() << endl;
  return 0;
}

代码解析

  1. 在i_traffic.h文件中,通过前置声明的形式来声明业务类Traffic
  2. 在i_traffic.cpp访问实体业务类的函数
  3. 在对外的接口文件i_traffic.h中,无法看到业务类的中的私有变量
使用Pimpl的优点:
  • 极大程度上减少了编译的依赖项,减少头文件的Include的次数;
  • 接口和实现分离;
  • 隐藏了业务类的实现;私有变量,私有函数等,外部人员拿到的接口类无从知晓业务类的头文件声明
  • 提高了代码的可阅读、可移植性;接口类专注对外暴漏,业务类专注与算法和逻辑,接口文件稳定性大大提高;
什么时候不适用Pimpl?
  • 如果类本身比较简单,使用Pimpl则会过度嵌套,消耗堆栈内存

参考连接

MSDN C++ PIMPL

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 设计技巧 Pimpl模式 的相关文章

  • 向进度条添加百分比文本 C#

    我有一个方法可以显示进程栏何时正在执行以及何时成功完成 我工作得很好 但我想添加一个百分比 如果完成 则显示 100 如果卡在某个地方 则显示更少 我在网上做了一些研究 但我无法适应我正在寻找的解决方案 这是我的代码 private voi
  • 在 C++ 中使用 matlab 结构(matlab 函数调用的返回值)(由 matlab 编译器生成的库)

    你好 我有一个相当简单的 matlab 函数 例如 function MYSTRUCT myfunc MYSTRUCT prop1 test MYSTRUCT prop2 foo MYSTRUCT prop3 42 end 我用 matla
  • Directory.Delete 之后 Directory.Exists 有时返回 true ?

    我有非常奇怪的行为 我有 Directory Delete tempFolder true if Directory Exists tempFolder 有时 Directory Exists 返回 true 为什么 可能是资源管理器打开了
  • 错误:表达式不产生值

    我尝试将以下 C 代码转换为 VB NET 但在编译代码时出现 表达式不产生值 错误 C Code return Fluently Configure Mappings m gt m FluentMappings AddFromAssemb
  • 当我们想要返回对象的引用时,为什么我们在赋值运算符中返回 *this 而通常(而不是 this)?

    我正在学习 C 和指针 我以为我理解了指针 直到我看到这个 一方面 asterix 运算符是解引用的 这意味着它返回值所指向的地址中的值 而与号 运算符则相反 它返回值存储的地址记忆 现在阅读有关赋值重载的内 容 它说 我们返回 this因
  • 使用 Newtonsoft 和 C# 反序列化嵌套 JSON

    我正在尝试解析来自 Rest API 的 Json 响应 我可以获得很好的响应并创建了一些类模型 我正在使用 Newtonsoft 的 Json Net 我的响应中不断收到空值 并且不确定我的模型设置是否正确或缺少某些内容 例如 我想要获取
  • 为什么 Google 测试会出现段错误?

    我是 Google Test 的新手 正在尝试提供的示例 我的问题是 当我引入失败并设置GTEST BREAK ON FAILURE 1 或使用命令行选项 GTest 将出现段错误 我正在考虑这个例子 https code google c
  • 回发后刷新时提示确认表单重新提交。我做错了什么?

    我有一个以空白 默认状态启动的仪表板 我让用户能够将保存的状态加载到仪表板中 当他们单击 应用 按钮时 我运行以下代码 function CloseAndSave var radUpload find radUpload1ID var in
  • 在 C 中初始化变量

    我知道有时如果你不初始化int 如果打印整数 您将得到一个随机数 但将所有内容初始化为零似乎有点愚蠢 我问这个问题是因为我正在评论我的 C 项目 而且我对缩进非常直接 并且它可以完全编译 90 90 谢谢 Stackoverflow 但我想
  • qdbusxml2cpp 未知类型

    在使用 qdbusxml2cpp 程序将以下 xml 转换为 Qt 类时 我收到此错误 qdbusxml2cpp c ObjectManager a ObjectManager ObjectManager cpp xml object ma
  • DbContext 和 ObjectContext 有什么区别

    From MSDN 表示工作单元和存储库模式的组合 使您能够查询数据库并将更改分组在一起 然后将这些更改作为一个单元写回存储 DbContext在概念上类似于ObjectContext 我虽然DbContext只处理与数据库的连接以及针对数
  • Qt - ubuntu中的串口名称

    我在 Ubuntu 上查找串行端口名称时遇到问题 如您所知 为了在 Windows 上读取串口 我们可以使用以下代码 serial gt setPortName com3 但是当我在 Ubuntu 上编译这段代码时 我无法使用这段代码 se
  • 如何在 Xaml 文本中添加电子邮件链接?

    我在 Windows Phone 8 应用程序中有一些大文本 我希望其中有电子邮件链接 例如 mailto 功能 这是代码的一部分
  • CMake 无法确定目标的链接器语言

    首先 我查看了this https stackoverflow com questions 11801186 cmake unable to determine linker language with c发帖并找不到解决我的问题的方法 我
  • Cmake 链接共享库:包含库中的头文件时“没有这样的文件或目录”

    我正在学习使用 CMake 构建库 构建库的代码结构如下 include Test hpp ITest hpp interface src Test cpp ITest cpp 在 CMakeLists txt 中 我用来构建库的句子是 f
  • C++ 函数重载类似转换

    我收到一个错误 指出两个重载具有相似的转换 我尝试了太多的事情 但没有任何帮助 这是那段代码 CString GetInput int numberOfInput BOOL clearBuffer FALSE UINT timeout IN
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • 当从finally中抛出异常时,Catch块不会被评估

    出现这个问题的原因是之前在 NET 4 0 中运行的代码在 NET 4 5 中因未处理的异常而失败 部分原因是 try finallys 如果您想了解详细信息 请阅读更多内容微软连接 https connect microsoft com
  • 如何将 PostgreSql 与 EntityFramework 6.0.2 集成? [复制]

    这个问题在这里已经有答案了 我收到以下错误 实体框架提供程序类型的 实例 成员 Npgsql NpgsqlServices Npgsql 版本 2 0 14 2 文化 中性 PublicKeyToken 5d8b90d52f46fda7 没

随机推荐