首页 > 其他分享 >设计模式之PIMPL模式

设计模式之PIMPL模式

时间:2024-11-23 18:55:47浏览次数:5  
标签:LineImpl Point int 模式 PIMPL 析构 Line 设计模式 构造函数

设计模式之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

标签:LineImpl,Point,int,模式,PIMPL,析构,Line,设计模式,构造函数
From: https://www.cnblogs.com/Invinc-Z/p/18564925

相关文章

  • 设计模式之 模板方法模式
    模板方法模式是行为型设计模式的一种。它定义了一个算法的骨架,并将某些步骤的实现延迟到子类中。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。模板方法模式的核心在于:封装算法的骨架:通过父类中的模板方法定义算法的执行顺序和框架,保证算法结构......
  • Redis如何保证高可靠性?(Redis集群模式)
    高可靠性数据尽量少丢失:AOF和RDB的持久化保证了数据尽量少丢失服务尽量少中断:增加副本冗余量通过如下3种集群模式保证高可靠性主从模式主从节点之间采用的是读写分离的方式,从节点只负责读操作,主节点负责读写操作,由主节点同步给从节点重要概念复制偏移量主从节点,......
  • Sigrity SPEED2000 DDR simulation模式如何生成和解读DDR仿真报告-SODIMM-Write模式
    SigritySPEED2000DDRsimulation模式如何生成和解读DDR仿真报告-SODIMM-Write模式SigritySPEED2000DDRsimulation模式如何进行DDR仿真分析操作指导-SODIMM-Write模式详细介绍了如何进行DDRWrite模式的仿真分析,下面基于此仿真结果进行DDR报告的输出和解读分析在workfl......
  • vue3下el-carousel的card模式下设置item之间的间距
    <el-carouselref="refCarousel"class="wh100Per":autoplay="false":loop="true"type="card"arrow="never"indicator-position="none"@change="priceChange">&......
  • 2024年最新互联网大厂精选 Java 面试真题集锦(JVM、多线程、MQ、MyBatis、MySQL、Redis
    前言春招,秋招,社招,我们Java程序员的面试之路,是挺难的,过了HR,还得被技术面,在去各个厂面试的时候,经常是通宵睡不着觉,头发都脱了一大把,还好最终侥幸能够入职一个独角兽公司,安稳从事喜欢的工作至今...近期也算是抽取出大部分休息的时间,为大家准备了一份通往大厂面试的小捷径,准备......
  • 基于对称点模式(symmetric dot pattern)的多元数据融合-matlab代码
        引言受最近深度学习在计算机视觉和语音识别方面的成功启发,许多研究者提出将一维时间序列数据编码为不同类型的图像,这样可以放大数据中的动态特性,更好地表征原数据。基于对称点模式(symmetricdotpattern)的多元数据融合对称点模式(SymmetrizedDotPattern,SDP)算法可......
  • 【深入浅出玩转FPGA】之FPGA配置模式
    FPGA配置模式XilinxUltraScaleFPGA有7种配置模式,由模式输入引脚M[2:0]决定。主串配置模式从串配置模式主并配置模式(8位或16位)从并配置模式(8位、16位或32位)主SPI配置模式主BPI配置模式JTAG/边界扫描配置模式1、主,即配置时钟CCLK由FPGA提供;从,即配置时钟CCLK由外部控制器提......
  • ActiveMQ消息模式Queue和Topic机制讲解
    Docker安装ActiveMQ镜像以及通过Java生产消费activemq示例_dockeractivemq-CSDN博客背景周末由于服务器异常宕机,导致业务系统重启后出现ActiveMQ中的数据没有被正常消费,运维认为是消息积压,便联系博主排查。最终发现并不存在消息积压,是因为采用ActiveMQTopic模式生产消费......
  • 面向对象与设计模式第一课:深入理解OOP
    第三章:面向对象与设计模式第一课:深入理解OOP面向对象编程(OOP)是一种编程范式,它将程序结构视为由对象组成,促进了代码的重用性和可维护性。在这一课中,我们将深入分析OOP的四个基本特性:封装、继承、多态和抽象,并提供相应的示例与实践。1.OOP基本特性1.1封装封装是OOP的核心......
  • PHP 正则表达式 修正符【m s x e ? (?i)】内部修正符 贪婪模式 后向引用 断言【总结篇
    1.正则表达式修正符在PHP中,正则表达式中的修正符(modifier)可以改变模式的行为,使得其功能更加灵活。1.m修正符(多行模式)作用:在多行模式下,^和$元字符除了匹配整个字符串的开头和结尾外,还可以匹配每一行的开头和结尾。举例: "Hello\nWorld",当使用/^World/m时,^会匹配"W......