C++基础:Pimpl设计模式的实现

2024/11/14:

        在实现C++17的Any类时偶然接触到了嵌套类的实现方法以及Pimpl设计模式,遂记录。

        PIMPL ( Private Implementation Pointer to Implementation )是通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏。

        通俗点来说,就是因为各种各样的原因(如:你不愿意让别人看到你的具体实现,你希望让头文件看起来更清爽)需要将底层的实现封装起来,只将API暴露出来。

        举个例子:

class Line
{
public:Line(int x1, int y1, int x2, int y2): _p1(x1, y1), _p2(x2, y2){}void showLine(){std::cout << "Line:" << std::endl;_p1.showPoint();_p2.showPoint();}
private:// 嵌套类放入private,专门为line服务class Point{public:~Point() = default;Point(int a, int b): _x(a), _y(b){}void showPoint() const{std::cout << "Point:" << std::endl;std::cout << _x << " " << _y << std::endl;}private:int _x, _y;};Point _p1, _p2;
};

         Line类对于那些只关注函数功能的用户来说可能会带来一定阅读成本。所以我们使用Pimpl设计模式,在这个类上面套一层壳子,将它隐藏起来。

        具体如下面代码所示:

// Line.h
#ifndef LINE_H
#define LINE_H
#include <iostream>
#endif
class OutLine
{
public:OutLine(int x1, int y1, int x2, int y2);~OutLine();void showOutLine();
private:// 嵌套类的前向声明class Line;Line * _Lineptr;
};
// Line.cpp
#include "Line.h"
class OutLine::Line
{
public:Line(int x1, int y1, int x2, int y2): _p1(x1, y1), _p2(x2, y2){}void showLine(){std::cout << "Line:" << std::endl;_p1.showPoint();_p2.showPoint();}
private:// 嵌套类放入private,专门为line服务class Point{public:~Point() = default;Point(int a, int b): _x(a), _y(b){}void showPoint() const{std::cout << "Point:" << std::endl;std::cout << _x << " " << _y << std::endl;}private:int _x, _y;};Point _p1, _p2;
};OutLine::OutLine(int x1, int y1, int x2, int y2)
: _Lineptr(new Line(x1, y1, x2, y2))
{std::cout << "create outLine" << std::endl;
}OutLine::~OutLine()
{std::cout << "delete outLine" << std::endl;delete _Lineptr;_Lineptr = nullptr;
}void OutLine::showOutLine() {if (_Lineptr) {_Lineptr->showLine();}
}

        可以看到,在Line.h文件中,我们使用了OutLine类作为套在Line类上的一层壳,并且成功隐藏了底层较为复杂的实现。

        下列代码用于测试:

// main.cpp
int main()
{OutLine l(1, 4, 2, 3);l.showOutLine();
}

        Pimpl设计模式主要用于将代码打包成头文件和库交给第三方使用。显而易见的,在类上再套一个类增加了一点点开销,但是相对于好处而言,这点代价是可以接受的。

        除了信息的隐藏,还有几点好处:

        1、只要头文件不变,可以实现对已实现的函数(参数列表不能变)功能的修改和优化。在修改之后,只需要替换动态库而不需要替换头文件。即可以实现库的平滑升级

        这一点是显而易见的,比如,当我们运行上面的测试代码时,会有以下输出:

create outLine
Line:
Point:
1 4
Point:
2 3
delete outLine

        可能我们觉得输出左对齐不是很美观,想美化一下输出,所以我们在Line.cpp文件中改一下Point类的输出函数:

void showPoint() const
{std::cout << "\tPoint: " <<  _x << " " << _y << std::endl;
}

        再运行一下测试代码,得到输出:

create outLine
Line:
        Point: 1 4
        Point: 2 3
delete outLine

        在这个修改过程中,并没有改动Line.h头文件。

         2、Pimpl最重要的功能:编译防火墙

        这一点其实跟上一点是相关联的,见下图:

reference c++库文件头文件链接原理(全)

        共享库(动态库)的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此生成的可执行程序代码体积较小。

        有些修改不会使头文件发生变化,只需要替换动态库(动态库是由cpp文件生成的),那么整个项目就不需要重新编译一遍。可以说这种设计模式完全发挥了动态库的优势。

        可能有些人对这个编译的时间开销没什么概念,这里举个不怎么恰当的例子:有些游戏只是修了个bug,可能就要让整个游戏重新下载。而注意这方面优化的游戏,可能下个几mb的补丁就结束了。

        前面的思路可能有点乱,这里总结一下采用Pimpl模式的优势与动态库的关系:

        当动态库更新时,通常情况下不需要重新编译可执行文件。因为在编译生成可执行文件时,链接器并不会将动态库的代码直接复制到可执行文件中,而只是记录了对该动态库的引用。程序在执行时会根据这些引用加载相应的动态库。如果该动态库已被加载,程序则不会重复加载,从而节省内存资源。

        然而,如果动态库的接口发生变化(例如函数的参数类型或返回值类型发生改变,或者函数被删除或重命名),那么可能需要重新编译可执行文件。在这种情况下,原有的可执行文件将无法正确调用动态库中的函数,可能会导致错误或异常。因此,Pimpl设计模式的编译防火墙优势就是建立在不去动这些接口的基础上的,这就要求项目有一个良好的设计思路,以尽量减少后期对接口的修改。

        可见Pimpl设计模式与动态库的匹配性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/59527.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从JVM的角度,来分析为什么Java中是值传递?

从 JVM 的角度来看&#xff0c;Java 中的参数传递之所以是值传递&#xff0c;是因为在 JVM 执行方法调用时&#xff0c;参数的值&#xff08;不论是基本类型还是引用类型&#xff09;都被复制并压入调用栈的帧&#xff08;stack frame&#xff09;中。让我们从 JVM 的内存模型和…

Navicat 17 功能简介 | 单元格编辑器

Navicat 17 功能简介 | 单元格编辑器 本期&#xff0c;我们一起了解 Navicat 17 出色的数据操作功能的单元格编辑器。单元格编辑器支持文本、十六进制、图像和网页四种格式的数据编辑&#xff0c;位于底部的编辑器窗格&#xff0c;为你编辑更大容量的数据信息提供足够的显示和操…

Unity自动LOD工具AutoLOD Mesh Decimator的使用

最近在研究大批量物体生成&#xff0c;由于我们没有专业美术&#xff0c;在模型减面工作上没有人手&#xff0c;所以准备用插件来实现LOD功能&#xff0c;所以找到了AutoLOD Mesh Decimator这个插件。 1&#xff0c;导入插件后&#xff0c;我们拿个实验的僵尸狗来做实验。 空…

爬虫补环境案例---问财网(rpc,jsdom,代理,selenium)

目录 一.环境检测 1. 什么是环境检测 2.案例讲解 二 .吐环境脚本 1. 简介 2. 基础使用方法 3.数据返回 4. 完整代理使用 5. 代理封装 6. 封装所有使用方法 jsdom补环境 1. 环境安装 2. 基本使用 3. 添加参数形式 Selenium补环境 1. 简介 2.实战案例 1. 逆向目…

免费,WPS Office教育考试专用版

WPS Office教育考试专用版&#xff0c;不仅满足了考试需求&#xff0c;更为教育信息化注入新动力。 https://pan.quark.cn/s/609ef85ae6d4

Vue前端开发,组件及组件的使用

什么是组件 组件(Component)是Vue中最强大的功能之一&#xff0c;每个Vue 文件就是一个个独立的组件&#xff0c;组件也可以被其他组件调用&#xff0c;形成嵌套关系&#xff0c;大部分的应用都是由各类不同功能的小组件进行构建&#xff0c;形成一个功能强大的大组件树系统&a…

Docker 中启动 NGINX 并配置 HTTPS 443 端口

在 Docker 中启动 NGINX 并配置 HTTPS 443 端口时&#xff0c;你需要挂载 SSL 证书和密钥文件&#xff0c;并更新 NGINX 配置文件。以下是详细步骤&#xff1a; 1. 准备证书文件 确保你有 SSL 证书和私钥文件&#xff0c;通常是两个文件&#xff1a; certificate.crt&#x…

政务数据治理专栏开搞!

写在前面 忙忙碌碌干了一年政务数据治理的工作&#xff0c;从法人数据到自然人&#xff0c;从交通到地理信息等等&#xff0c;突发想法开一个专栏讲一讲政务数据遇到的问题&#xff0c;以及治理的成效&#xff0c;或许有朋友爱看。 政务数据&#xff0c;又称之为政务数据资源&a…

前端在PC端实现支付思路流程

一.去支付 1.前端点击“去支付”按钮&#xff0c;请求订单详情接口&#xff0c;传递订单的id、订单号给后端和请求支付方式接口 2.后端返回支付信息和支付方式数据 二.弹出支付窗口 接收支付信息和支付方式数据后&#xff0c;前端弹出支付弹窗 三.确认支付 前端无论选择任何…

The 3rd Universal CupStage 15: Chengdu, November 2-3, 2024(2024ICPC 成都)

Problem L. Recover Statistics 题目意思&#xff1a; 给定a, b, c三个值&#xff0c;确保构造的数列中包含满足题目的数量 解题思路&#xff1a; 100 中 选择a 50个&#xff0c; b45个&#xff0c; c4个。 #include <iostream>using namespace std;using ll long …

VUE3实现好看的世界建筑中国建筑网站源码

文章目录 1.设计来源1.1 网站主界面1.2 登录界面1.3 注册界面1.4 特色建筑展览界面1.5 世界建筑介绍界面1.6 世界建筑介绍 - 详情界面1.7 中国建筑介绍界面1.8 中国建筑介绍 - 详情界面1.9 关于我们界面 2.效果和源码2.1 动态效果2.2 源代码2.3 目录结构 源码下载万套模板&…

「人眼视觉不再是视频消费的唯一形式」丨智能编解码和 AI 视频生成专场回顾@RTE2024

你是否想过&#xff0c;未来你看到的电影预告片、广告&#xff0c;甚至新闻报道&#xff0c;都可能完全由 AI 生成&#xff1f; 在人工智能迅猛发展的今天&#xff0c;视频技术正经历着一场前所未有的变革。从智能编解码到虚拟数字人&#xff0c;再到 AI 驱动的视频生成&#…

「QT」文件类 之 QTemporaryFile 临时文件类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定制…

和 Nostr 探索 Web5 的未来

Nostr 是一个我过去两年一直在关注的协议。跟所有社区一样&#xff0c;Nostr 的发展也是起起伏伏&#xff0c;有过一些破圈被主流熟悉的时刻&#xff0c;也有一些像现在这样可能让人会觉得有点沉寂的时刻。但我还是经常关注 Nostr&#xff0c;没有特别的原因&#xff0c;就是单…

提高数据处理效率:JavaScript 操作 XLSX 文件的最佳实践

前言 Excel 文件&#xff08;通常以 .xlsx 为后缀&#xff09;在各种业务场景中都有广泛应用&#xff0c;如数据分析、财务报告和其他数据表单处理。掌握如何在前端或后端使用 JavaScript 对这些 Excel 文件进行读取、创建和修改&#xff0c;能够显著提升我们的开发效率和数据…

使用Python实现对接Hadoop集群(通过Hive)并提供API接口

安装必要的库 首先&#xff0c;确保已经安装了以下库&#xff1a; pip install flask pip install pyhive代码实现 1. app.py&#xff08;主应用文件&#xff09; from flask import Flask, jsonify, request, abort from pyhive import hive import re from datetime impo…

论文学习——一种基于决策变量分类的动态约束多目标进化算法

论文题目&#xff1a; A dynamic constrained multiobjective evolutionary algorithm based on decision variable classification 一种基于决策变量分类的动态约束多目标进化算法&#xff08;Yinan Guo a,b, Mingyi Huang a, Guoyu Chen a,*, Dunwei Gong c, Jing Liang d, …

Vue计算属性computed

在 Vue 中&#xff0c;计算属性&#xff08;computed properties&#xff09;是基于已有数据进行计算得出的属性&#xff0c;通常用于需要根据已有的 data 或其他属性来动态计算值时。 基本语法 计算属性通过 Vue 实例的 computed 选项来定义。与方法不同&#xff0c;计算属性…

数据分析案例-笔记本电脑价格数据可视化分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

第T7周:Tensorflow实现咖啡豆识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标 具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python 3.10 编 译 器: PyCharm 框 架: &#xff08;二&#xff09;具体步骤 1. 使…