当前位置: 首页 > news >正文

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 的对话&#xff0c;很精彩的回答 You 我有个问题&#xff0c;我的 main 目标依赖 src/gcp_subscriber.h 的 GCPSubscriber class 这个 class 有个 private 成员 google::cloud::pubsub::Subscriber 也就意味着我得在 gcp_subscriber.h 里面引用 google clou…...

【uniapp】小程序开发8:滚动组件scroll-view

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

Java王者荣耀火柴人

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

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版本及以上的应用及服务开发&#xff0c;提供了代码智能编辑、低代…...

JDK多版本集成 Jacoco 配置指南

JDK多版本集成 Jacoco 配置指南 本篇相关 JDK 版本配置如下&#xff1a; JDK8 JDK11 JDK17 Jacoco 是什么 Jacoco 是一个用于Java程序的代码覆盖率报告工具。它通过动态分析&#xff08;在代码执行时收集数据&#xff09;来生成代码覆盖率报告文件。Jacoco 支持多种覆盖率标…...

容器及容器调度(云)

在云计算中&#xff0c;容器是一种轻量级、可执行的软件包&#xff0c;它包含应用程序及其全部依赖项&#xff0c;包括库、二进制文件、配置文件等。容器与虚拟机不同&#xff0c;因为它们不需要包含完整的操作系统&#xff1b;相反&#xff0c;所有容器都共享主机操作系统的内…...

实验七 子网的划分

实验七 子网的划分 实验目的掌握划分子网的方法实验内容划分给定IP地址的子网将划分后的子网应用到网络环境中实验要求每位同学从下表中至少选择一行进行子网划分,并填写所选择行的剩余部分。(注意:子网号全0的不用)标准IP地址 要求划 分子网数 借用的主机位数 子网掩码 第…...

Proteus仿真--射击小游戏仿真设计

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

docker的资源控制:

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

Leo赠书活动-13期 【以企业架构为中心的SABOE数字化转型五环法】文末送书

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

【人工智能 | 知识表示方法】状态空间法 语义网络,良好的知识表示是解题的关键!(笔记总结系列)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…...

华清远见嵌入式学习——QT——作业1

作业要求&#xff1a; 代码&#xff1a; ①&#xff1a;头文件 #ifndef LOGIN_H #define LOGIN_H#include <QWidget> #include <QLineEdit> //行编辑器类 #include <QPushButton> //按钮类 #include <QLabel> //标签类 #include <QM…...

MYSQL练习创建存储函数和存储过程

创建数据表&#xff0c;信息如下&#xff1a; 表结构&#xff1a; 字段名 数据类型 主键 外键 非空 唯一 自增 id INT 是 否 是 是 否 name VARCHAR(50) 否 否 是 否 否 glass VARCHAR(50) 否 否 是 否…...

Java基础语法面试题

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

结合ColorUI组件开发微信小程序

1.自定义组件生命周期函数&#xff1a; Component({data: {},attached() {console.log("自定义组件生命周期函数 attached--先执行");this.getPos();},ready() {console.log("ready生命周期函数---在attached之后执行")},methods: {getPos() {var that th…...

如何搭建废品上门回收小程序

如今&#xff0c;随着环境保护意识的增强&#xff0c;废品的回收和再利用变得越来越重要。为了方便人们进行废品回收&#xff0c;搭建一个废品上门回收的小程序成为了一个不错的选择。本文将介绍如何从零开始搭建一个废品上门回收小程序。 …...

蓝牙配对、连接和删除汇总

目的&#xff1a;处理PC连接蓝牙设备的配对、连接和删除操作&#xff0c;以及常见故障。 命令行配对蓝牙设备并连接 要求&#xff1a;配对BLUET043蓝牙&#xff0c;密码为4444&#xff0c;然后连接该蓝牙。操作步骤如下&#xff1a; Step1.下载 修复工具&#xff0c;然后安装…...

Linux网络——高级IO

目录 一.五种IO模型 1.阻塞式IO 2.非阻塞式IO 3.信号驱动IO 4.多路转接IO&#xff1a; 5.异步IO 二.同步通信 vs 异步通信 三.设置非阻塞IO 1.阻塞 vs 非阻塞 2.非阻塞IO 3.实现函数SetNoBlock 四.I/O多路转接之select 1.初识select 2.select函数原型 3.socket就绪…...

Java注解详解

概述 注解是对程序代码进行标注和解释的一种方式。在Java中&#xff0c;注解提供了一种元数据形式&#xff0c;能够在程序中嵌入有关程序的信息&#xff0c;以便进行进一步的处理。注解通过使用符号来声明&#xff0c;如Override、Deprecated等。 注解和注释的区别 注释&…...

Android wifi 框架以及Enable流程

Android P相比于Android O的变化 多了WifiStateMachinePrime&#xff08;状态机的前处理机制&#xff09;&#xff0c;wifiService的相关cmd 不再是直接send 给WifiStateMachine&#xff0c;而是被送到WifiStateMachinePrime先进行处理后&#xff0c;再送往WifiStateMachine也…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...