C++ 设计模式 Forward Declaration Pimpl
放几轮跟 chatgpt 的对话,很精彩的回答
You
我有个问题,我的 main 目标依赖 src/gcp_subscriber.h 的 GCPSubscriber class
这个 class 有个 private 成员 google::cloud::pubsub::Subscriber
也就意味着我得在 gcp_subscriber.h 里面引用 google cloud 的依赖库。
也就意味着 main 也会 include 到 google cloud 依赖库,这会减慢我的 main 目标编译速度
ChatGPT
在 C++ 中,使用前置声明(forward declaration)可以避免引入完整的头文件而只引入所需的类的声明。通过使用前置声明,你可以减少编译时需要处理的代码量,从而提高编译速度。
在你的 gcp_subscriber.h 中,尝试使用前置声明而不是包含整个 google/cloud/pubsub/subscriber.h 头文件。只在实现文件中(例如 gcp_subscriber.cpp)引入完整的头文件。这样可以将依赖库的具体实现细节延迟到编译阶段的实现文件中,从而加速主目标的编译。
以下是一个示例:
gcp_subscriber.h:
#ifndef GCP_SUBSCRIBER_H_
#define GCP_SUBSCRIBER_H_#include <memory> // 如果使用了 std::shared_ptr// Forward declaration of google::cloud::pubsub::Subscriber
namespace google::cloud::pubsub {
class Subscriber;
} // namespace google::cloud::pubsubclass GCPSubscriber {
public:GCPSubscriber(std::string channel, std::shared_ptr<MessageBufferQueue> queue);void block_receive();private:std::shared_ptr<MessageBufferQueue> queue;std::unique_ptr<google::cloud::pubsub::Subscriber> subscriber;
};#endif // GCP_SUBSCRIBER_H_
gcp_subscriber.cpp:
#include "gcp_subscriber.h"
#include <google/cloud/pubsub/subscriber.h>GCPSubscriber::GCPSubscriber(std::string channel, std::shared_ptr<MessageBufferQueue> queue): queue(queue),subscriber(std::make_unique<google::cloud::pubsub::Subscriber>(google::cloud::pubsub::MakeSubscriberConnection(google::cloud::pubsub::Subscription("combo-001", fmt::format("cme-mktdata-{}-NR", channel)))))
{
}void GCPSubscriber::block_receive() {// 实现 block_receive 函数的具体内容// ...
}
通过这种方式,gcp_subscriber.h 只需要知道 google::cloud::pubsub::Subscriber
的存在,而不需要知道它的具体实现细节。这有助于减少 main 目标的编译时间,因为它不再需要处理完整的 google/cloud/pubsub/subscriber.h。
省略中间一些对话
You
那这前置声明不就失效了吗
ChatGPT
你是对的,我为之前的回答道歉。由于需要在 gcp_subscriber.h 中的实现文件 (gcp_subscriber.cc) 中使用完整的 google::cloud::pubsub::Subscriber
,前置声明就不再适用了。
在这种情况下,你需要在 gcp_subscriber.h 中包含完整的头文件,以便确保 google::cloud::pubsub::Subscriber
的定义是可见的。这将导致包含 Google Cloud Pub/Sub 相关的头文件,从而增加了编译时间。解决方案是确保只在需要的地方包含这些头文件,以尽量减小编译时间的影响。
如果你的项目结构允许,可以考虑将 google::cloud::pubsub::Subscriber
的具体实现移到实现文件(如 gcp_subscriber.cc)中,而不是将其定义放在头文件中。这样,头文件只需要进行前置声明,而不必包含完整的定义。这样可以减小头文件的依赖,提高编译速度。例如:
gcp_subscriber.h:
#ifndef GCP_SUBSCRIBER_H_
#define GCP_SUBSCRIBER_H_#include <memory>class MessageBufferQueue; // 前置声明class GCPSubscriber {
public:GCPSubscriber(std::string channel, std::shared_ptr<MessageBufferQueue> queue);void block_receive();private:std::shared_ptr<MessageBufferQueue> queue;class Impl; // 前置声明std::unique_ptr<Impl> subscriber_impl;
};#endif // GCP_SUBSCRIBER_H_
gcp_subscriber.cc:
#include "gcp_subscriber.h"
#include <google/cloud/pubsub/subscriber.h>class GCPSubscriber::Impl {
public:google::cloud::pubsub::Subscriber subscriber;// ... 其他成员和实现
};GCPSubscriber::GCPSubscriber(std::string channel, std::shared_ptr<MessageBufferQueue> queue): queue(queue), subscriber_impl(std::make_unique<Impl>())
{// 对 Impl 进行初始化,包括创建 google::cloud::pubsub::Subscriber
}void GCPSubscriber::block_receive() {// 实现 block_receive 函数的具体内容,使用 subscriber_impl->subscriber// ...
}
这种方式可以将 Google Cloud Pub/Sub 的具体实现从头文件中移除,从而减小编译时的依赖。
You
这是 pimpl 吗
ChatGPT
是的,这种技术被称为 Pimpl(Pointer to Implementation)或者又称为 Opaque Pointer。Pimpl 是一种设计模式,其主要目标是将类的实现细节(成员变量、成员函数的实现等)隐藏在一个独立的类中,以减小头文件的依赖,提高封装性,同时降低编译时的依赖性。
在 Pimpl 模式中,类的头文件只包含对实现类的前置声明,而实现类的定义则放在实现文件中。这样,只有在实现文件中才需要包含具体的头文件,而头文件中则只需要包含前置声明,从而减小了编译时的依赖。
这对于减小编译时间和隐藏实现细节非常有用。同时,当实现发生变化时,只需要修改实现文件,而头文件可以保持不变,从而减小了对外部代码的影响。
在你的例子中,GCPSubscriber 类的具体实现被封装在 GCPSubscriber::Impl 类中,而 GCPSubscriber 的头文件中只包含了 MessageBufferQueue 的前置声明和 Impl 的前置声明,从而隐藏了 google::cloud::pubsub::Subscriber
的具体实现。
相关文章:
C++ 设计模式 Forward Declaration Pimpl
放几轮跟 chatgpt 的对话,很精彩的回答 You 我有个问题,我的 main 目标依赖 src/gcp_subscriber.h 的 GCPSubscriber class 这个 class 有个 private 成员 google::cloud::pubsub::Subscriber 也就意味着我得在 gcp_subscriber.h 里面引用 google clou…...

【uniapp】小程序开发8:滚动组件scroll-view
我们经常需要做页面中部分内容可以滚动的功能,例如“猜你喜欢”,内容太多,通常都会超出屏幕,那么此块区域应该可以滚动,但是顶部的自定义导航栏应该不能随着滚动。 这个时候,就可以使用uniapp提供的滚动组件…...

Java王者荣耀火柴人
主要功能 键盘W,A,S,D键:控制玩家上下左右移动。按钮一:控制英雄发射一个矩形攻击红方小兵。按钮控制英雄发射魅惑技能,伤害小兵并让小兵停止移动。技能三:攻击多个敌人并让小兵停止移动。普攻:对小兵造成基础伤害。小…...

1.鸿蒙应用程序开发app_hap开发环境搭建
1.下载Node.js, Javascipts的运行环境 node.js版本下载v12.18.3/https://www.cnblogs.com/txwtech/p/17865780.html 2.下载并安装DevEco Studio DevEco Studio 3.1 DevEco Studio 3.1配套支持HarmonyOS 3.1版本及以上的应用及服务开发,提供了代码智能编辑、低代…...

JDK多版本集成 Jacoco 配置指南
JDK多版本集成 Jacoco 配置指南 本篇相关 JDK 版本配置如下: JDK8 JDK11 JDK17 Jacoco 是什么 Jacoco 是一个用于Java程序的代码覆盖率报告工具。它通过动态分析(在代码执行时收集数据)来生成代码覆盖率报告文件。Jacoco 支持多种覆盖率标…...
容器及容器调度(云)
在云计算中,容器是一种轻量级、可执行的软件包,它包含应用程序及其全部依赖项,包括库、二进制文件、配置文件等。容器与虚拟机不同,因为它们不需要包含完整的操作系统;相反,所有容器都共享主机操作系统的内…...
实验七 子网的划分
实验七 子网的划分 实验目的掌握划分子网的方法实验内容划分给定IP地址的子网将划分后的子网应用到网络环境中实验要求每位同学从下表中至少选择一行进行子网划分,并填写所选择行的剩余部分。(注意:子网号全0的不用)标准IP地址 要求划 分子网数 借用的主机位数 子网掩码 第…...

Proteus仿真--射击小游戏仿真设计
本文介绍基于proteus射击小游戏仿真设计(完整仿真源文件及代码见文末链接) 仿真图如下 K1-K4为4个按键,用于上移、下移、确认等,模拟单机游戏 仿真运行视频 Proteus仿真--射击小游戏仿真设计 附完整Proteus仿真资料代码资料 …...

docker的资源控制:
docker的资源控制: 对容器的使用宿主机的资源进行限制 cpu 内存 磁盘i/0 docker使用linux自带的功能cgroup control grouos是linux内核系统提供的一种可以限制,记录,隔离进程所使用的物理资源 control grouos是linux内核系统提供的一种可…...

Leo赠书活动-13期 【以企业架构为中心的SABOE数字化转型五环法】文末送书
Leo赠书活动-13期 【以企业架构为中心的SABOE数字化转型五环法】文末送书 ✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客…...

【人工智能 | 知识表示方法】状态空间法 语义网络,良好的知识表示是解题的关键!(笔记总结系列)
🤵♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…...

华清远见嵌入式学习——QT——作业1
作业要求: 代码: ①:头文件 #ifndef LOGIN_H #define LOGIN_H#include <QWidget> #include <QLineEdit> //行编辑器类 #include <QPushButton> //按钮类 #include <QLabel> //标签类 #include <QM…...
MYSQL练习创建存储函数和存储过程
创建数据表,信息如下: 表结构: 字段名 数据类型 主键 外键 非空 唯一 自增 id INT 是 否 是 是 否 name VARCHAR(50) 否 否 是 否 否 glass VARCHAR(50) 否 否 是 否…...

Java基础语法面试题
数据类型 Java有哪些数据类型 定义:Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类 型,在内存中分配了不同大小的内存空间。 分类: 基本数据类型 数值型 整数类型(byte,short,int,long) 浮点类型(float,dou…...

结合ColorUI组件开发微信小程序
1.自定义组件生命周期函数: Component({data: {},attached() {console.log("自定义组件生命周期函数 attached--先执行");this.getPos();},ready() {console.log("ready生命周期函数---在attached之后执行")},methods: {getPos() {var that th…...

如何搭建废品上门回收小程序
如今,随着环境保护意识的增强,废品的回收和再利用变得越来越重要。为了方便人们进行废品回收,搭建一个废品上门回收的小程序成为了一个不错的选择。本文将介绍如何从零开始搭建一个废品上门回收小程序。 …...
蓝牙配对、连接和删除汇总
目的:处理PC连接蓝牙设备的配对、连接和删除操作,以及常见故障。 命令行配对蓝牙设备并连接 要求:配对BLUET043蓝牙,密码为4444,然后连接该蓝牙。操作步骤如下: Step1.下载 修复工具,然后安装…...

Linux网络——高级IO
目录 一.五种IO模型 1.阻塞式IO 2.非阻塞式IO 3.信号驱动IO 4.多路转接IO: 5.异步IO 二.同步通信 vs 异步通信 三.设置非阻塞IO 1.阻塞 vs 非阻塞 2.非阻塞IO 3.实现函数SetNoBlock 四.I/O多路转接之select 1.初识select 2.select函数原型 3.socket就绪…...
Java注解详解
概述 注解是对程序代码进行标注和解释的一种方式。在Java中,注解提供了一种元数据形式,能够在程序中嵌入有关程序的信息,以便进行进一步的处理。注解通过使用符号来声明,如Override、Deprecated等。 注解和注释的区别 注释&…...

Android wifi 框架以及Enable流程
Android P相比于Android O的变化 多了WifiStateMachinePrime(状态机的前处理机制),wifiService的相关cmd 不再是直接send 给WifiStateMachine,而是被送到WifiStateMachinePrime先进行处理后,再送往WifiStateMachine也…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

网页端 js 读取发票里的二维码信息(图片和PDF格式)
起因 为了实现在报销流程中,发票不能重用的限制,发票上传后,希望能读出发票号,并记录发票号已用,下次不再可用于报销。 基于上面的需求,研究了OCR 的方式和读PDF的方式,实际是可行的ÿ…...