设计模式之PIMPL模式

news/2025/2/26 13:12:28/文章来源:https://www.cnblogs.com/Invinc-Z/p/18564925

设计模式之PIMPL模式

PIMPL是指pointer to implementation,又称作“编译防火墙”,是实现“将文件间的编译依存关系降至最低”的方法之一。PIMPL模式是一种减少代码依赖和编译时间的C++编程技巧,其基本思想是将一个外部可见类的实现细节(一般是通过私有的非虚成员)放在一个单独的实现类中,在可见类中通过一个私有指针来间接访问该类型。

一、PIMPL模式实现

代码实现

实际项目的需求:希望Line类的实现全部隐藏,在源文件中实现,再将其打包成库文件,交给第三方使用。

  1. Line.hpp 头文件只给出接口
#pragma once
class Line
{
public:Line(int x1,int y1, int x2, int y2);~Line();void printLine() const; // 打印 Line 对象的信息private:class LineImpl; // 类的前向声明LineImpl* _pImpl;
};
  1. LineImpl.cc 在实现文件中进行具体实现,使用嵌套类的结构(LineImpl是Line的内部类,Point是LineImpl的内部类),Line类对外公布的接口都是使用LineImpl进行具体实现的。
#include "Line.hpp"
#include <iostream>
using namespace std;class Line::LineImpl{
public:class Point{public:Point(int x, int y);~Point();void print() const;private:int _ix;int _iy;};
public:LineImpl(int x1, int y1, int x2, int y2);~LineImpl();void printLine() const;
private:Point _pt1;Point _pt2;
};Line::LineImpl::Point::Point(int x,int y):_ix(x),_iy(y){cout << "Point(int,int)" << endl;
}
Line::LineImpl::Point::~Point(){cout << "~Point()" << endl;
}
void Line::LineImpl::Point::print() const{cout << "(" << _ix << "," << _iy << ")";
}Line::LineImpl::LineImpl(int x1, int y1, int x2, int y2):_pt1(x1,y1),_pt2(x2,y2){cout << "Line::LineImpl::LineImpl(int,int,int,int)" << endl;
}
Line::LineImpl::~LineImpl(){cout << "Line::LineImpl::~LineImpl()" << endl;
}
void Line::LineImpl::printLine() const{_pt1.print();cout << "--->";_pt2.print();cout << endl;
}Line::Line(int x1, int y1, int x2, int y2):_pImpl(new LineImpl(x1,y1,x2,y2)){cout << "Line::Line(int*4)" << endl;
}
Line::~Line(){cout << "Line::~Line()" << endl;if(_pImpl){delete _pImpl;_pImpl = nullptr;}
}
void Line::printLine() const{_pImpl->printLine();
} 
  1. LineTest.cc 在测试文件中创建Line对象(最外层),使用Line对外提供的接口,但是不知道具体的实现。
#include "Line.hpp"
#include <iostream>void test0(){Line line(1,2,3,4);line.printLine();
}
int main()
{test0();                                                                                     return 0;
}
  1. 打包库文件,将库文件和头文件交给第三方
g++ -c LineImpl.cc
ar rcs libLine.a LineImpl.o生成 libLine.a 库文件
编译:g++ Line.cc(测试文件) -L(加上库文件地址) -lLine(就是库文件名中的 lib 缩写为 l,不带后缀)
此时的编译指令为 g++ Line.cc -L. -lLine

内存结构

image-20241123173924894

构造析构顺序分析

运行程序结果如下:

image-20241123173117230

Line line(1,2,3,4);Line::Line(int x1, int y1, int x2, int y2):_pImpl(new LineImpl(x1,y1,x2,y2)){cout << "Line::Line(int*4)" << endl;
}Line::LineImpl::LineImpl(int x1, int y1, int x2, int y2):_pt1(x1,y1),_pt2(x2,y2){cout << "Line::LineImpl::LineImpl(int,int,int,int)" << endl;
}Line::LineImpl::Point::Point(int x,int y):_ix(x),_iy(y){cout << "Point(int,int)" << endl;
}

构造顺序分析:首先 line调用Line的构造函数,在line构造函数的初始化列表中调用LineImpl的构造函数,然后再在LineImpl的构造函数初始化列表中执行Point的构造函数,Point类的构造函数执行完成之后,返回继续执行LineImpl的构造函数的函数体部分,在LineImpl构造执行完成之后,继续执行Line的函数体部分。

void test0(){Line line(1,2,3,4);
}Line::LineImpl::Point::~Point(){cout << "~Point()" << endl;
}Line::LineImpl::~LineImpl(){cout << "Line::LineImpl::~LineImpl()" << endl;
}Line::~Line(){cout << "Line::~Line()" << endl;if(_pImpl){delete _pImpl;_pImpl = nullptr;}
}

析构顺序分析:在line离开test0()函数的右括号之后,line的生命周期结束,开始执行Line的析构函数,首先输出Line析构的提示信息,接着delete指向LineImpl的指针。在delete的操作中,首先调用该指针指向对象LineImpl的析构函数,以完成资源清理工作,Point作为LineImpl的数据成员子对象在其父对象析构时也开始析构,析构完成后回到LineImpl完成析构收尾工作,当LineImpl析构完成后,delete 进行第二步工作,释放对象占用的内存,最后Line构造执行结束。

总结:构造析构的顺序都是从父对象开始执行,其顺序可以想象成一个环形。

二、PIMPL模式优点与目的:

  1. 信息隐藏
    私有成员完全可以隐藏在共有接口之外,尤其对于闭源API的设计尤其的适合。同时,很多代码会应用平台依赖相关的宏控制,这些琐碎的东西也完全可以隐藏在实现类当中,给用户一个简洁明了的使用接口。

  2. 加速编译
    这通常是用pImpl手法的最重要的收益,称之为编译防火墙(compilation firewall),主要是阻断了类的接口和类的实现两者的编译依赖性。这样,类用户不需要额外include不必要的头文件,同时实现类的成员可以随意变更,而公有类的使用者不需要重新编译。

  3. 更好的二进制兼容性
    对于使用pImpl手法,只要头文件中的接口不变,实现文件可以随意修改,修改完毕只需要将新生成的库文件交给第三方即可,所以实现做出重大变更的情况下,pImpl也能够保证良好的二进制兼容性,可以实现库的平滑升级,这是pImpl的精髓所在。

  4. 惰性分配
    实现类可以做到按需分配或者实际使用时候再分配,从而节省资源提高响应。

参考博文:

https://www.cnblogs.com/sggggr/p/16939996.html

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

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

相关文章

Mac开发_制作.dmg压缩镜像文件

创建一个文件夹打开终端进入此文件夹 cd /Users/gc/Desktop/GC_Codes/打包dmg专用/Ocer_1.0.0_202411231800 使用命令生成application替身,执行以下命令 ln -s /Applications/ Applications.app文件放入文件夹打开磁盘工具:新建映像->选择基于文件夹新建映像选择这个文件…

PTA第4~6次大作业分析及总结

一、前言 ​ 本次Blog为第四~六次PTA的总结,历时三周,主要考察我们对继承的掌握情况,通过完成这几次PTA,我对java的继承的使用有了了解,这三次大作业较之前的大作业难度进一步提升,尤其是第六次大作业,由于之前生病了,我没有投入几天的精力解决这次PTA,在最后一天没能及…

SolidWorks一键生成BOM到Excel里,并可选择带缩略图

此前提到有粉丝朋友分享给我的一个VBA宏程序,能根据Solidworks装配体的零件属性一键导出到Excel,并可选择是否需要缩略图,零件的属性可以自定义,是一个很好的程序。但是试用后,发现有一些问题,其中最大的问题,也是很多粉丝朋友提到的一个问题,无法选择只导出子装配体或…

java二阶段总结(电路模拟程序)

前言第四次题目集知识点:开始应用继承在大题目中,但对于方法重写,多态的要求未涉及继承是面向对象的三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。 继承是指在原有类的基础上,进行功能扩展,创建新的类型。方法重载:第一题s…

南昌航空大学-软件学院-22207112-卢翔-JAVAPTA(4-6)博客

前言 PTA第四次作业 设计与分析 题目分析 本题在答题判题程序3基础上新增了选择题和填空题内容,程序输入信息分五种, 输入格式: 1、题目信息格式:"#N:"+题目编号+" "+"#Q:"+题目内容+" "#A:"+标准答案 2、试卷信息格式:"…

js和CSS3自定义鼠标特效

在线预览 下载这是一款js和CSS3自定义鼠标特效。该特效中,使用一个DIV元素来自定义鼠标,通过CSS代码来构建鼠标的形状,并通过js代码来驱动鼠标的动画。使用方法HTML结构 基本的HTML结构如下。<!--鼠标形状元素--> <div id="cursorBlob"></div>…

ArcGIS填补面图层的细小空白并删除主体部分外的零散部分

本文介绍在ArcMap软件中,基于消除面部件(“Eliminate Polygon Part”)工具,对矢量面要素的零碎、空洞区域加以删除,同时将游离于要素主体之外的部分剔除的方法~本文介绍在ArcMap软件中,基于消除面部件(“Eliminate Polygon Part”)工具,对矢量面要素的零碎、空洞区域加…

JetBrains IDE 2024.3 (macOS, Linux, Windows) - 开发者工具

JetBrains IDE 2024.3 (macOS, Linux, Windows) - 开发者工具JetBrains IDE 2024.3 (macOS, Linux, Windows) - 开发者工具 Aqua, CLion, DataGrip, DataSpell, Fleet, GoLand, IntelliJ IDEA, PhpStorm, PyCharm, Rider, RubyMine, WebStorm 请访问原文链接:https://sysin.or…

OO4-6次作业作业总结

总结性博客:Java题目习题集4-6学习心得 一、前言 通过三次习题集的练习,我对面向对象编程(OOP)、集合框架和输入输出操作有了更加深刻的理解。这三次题目涵盖的内容主要包括以下几个方面:题目知识点: 第一次习题集:以Java的继承与多态为核心,强化对类与对象的理解,设计…

如何立项?Tita 项目审批管控

什么场景下企业需要「项目审批」? 如果你的企业不允许员工随意创建项目,项目的创建需要走「立项审批」,也不允许员工随意修改项目时间,或完成项目、删除项目等,你就可以使用「项目审批」,从而达到对项目的创建和修改进行严格管控的目的。 如何使用项目审批?第一步:在后…

《DNK210使用指南 -CanMV版 V1.0》第三十九章 YOLO2人脸检测实验

第三十九章 YOLO2人脸检测实验 1)实验平台:正点原子DNK210开发板 2)章节摘自【正点原子】DNK210使用指南 - CanMV版 V1.0 3)购买链接:https://detail.tmall.com/item.htm?&id=782801398750 4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/k…

2024-2025-1 20241407《计算机基础与程序设计》第九周学习总结

作业信息这个作业属于哪个课程 2024-2025-1 计算机基础与程序设计这个作业要求在哪里 2024-2025-1 计算机基础与程序设计第九周作业这个作业的目标 操作系统责任,内存与进程管理,分时系统,CPU调度,文件、文件系统,文件保护,磁盘调度作业正文 本博客教材学习内容总结 《计…