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也…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...
