Qt-D指针与Q指针的设计哲学
文章目录
- 前言
- PIMLP与二进制兼容性
- D指针
- Q指针
- 优化d指针继承
- Q_D和Q_Q
前言
在探索Qt源码的过程中会看到类的成员有一个d指针,d指针类型是一个private的类,这种设计模式称为PIMPL(pointer to implementation),本文根据Qt官方文章介绍d指针与q指针,理解其中的设计哲学。
PIMLP与二进制兼容性
PIMPL(pointer to implementation)或称Opaque Pointer指的是将一个类的功能实现通过另一个实现类的指针隐藏起来,例如头文件有如下声明:
class Widget {public:Widget();...
private:WidgetImpl* p;
}
在cpp中实现WidgetImpl类
class WidgetImpl {// implementation
}
这种设计模式通常使用在库的实现当中,因为它有两个好处:
- 隐藏类实现。包括一些没必要暴露给用户的内部函数声明,以及需要的成员变量等实现细节。
- 库的二进制兼容。当改变实现类的数据成员时,不影响库的二进制兼容性,原本使用库的主程序不需要重新编译,只需要把库文件(动态库)进行替换即可。
让我们看一个二进制不兼容的例子,假设有如下实现,并且将其编译成WidgetLib1.0动态库:
class Widget{// ...private:Rect m_geometry;};class Label : public Widget{public:// ... String text() const {return m_text;}private:String m_text;}
当我们有一天希望升级Widget类的功能,需要新增一个数据成员,如下:
class Widget{// ...private:Rect m_geometry;String m_stylesheet; // NEW in WidgetLib 1.1};class Label : public Widget{public:// ...String text() const{return m_text;}private:String m_text;};
此时编译出WidgetLib1.1库后,替换1.0库,这时运行主程序会发生崩溃。原因在于我们新增了一个数据成员,从而改变了类Widget的大小,当编译器在编译生成底层代码时,它会使用到数据成员的偏移量从而访问某个对象的某一个数据成员,下面是WidgetLib1.0和WidgetLib1.1简化后label对象的内存分布对比。
在WidgetLib1.0中,m_text在label对象偏移量为1的位置,而在WidgetLib1.1中,m_text偏移量为2。对于主程序而言,在编译使用1.0版本的主程序时,主程序中调用text()接口的代码会被翻译成访问label对象偏移量为1的位置的数据,而在升级到WidgetLib1.1后,由于主程序没有重新编译,只是替换了库,库中的m_text偏移量变为了2,但是主程序由于没有重新编译,因此它访问的仍然是偏移量为1的位置,但是此时访问到的实际上是m_stylesheet的变量。
这里text()的代码实现的翻译在主程序中而不是在lib中,是因为其实现是在头文件中写的,那么如果不是写在头文件中结果有变化吗?答案是没有,因为编译器依赖于对象的大小生成代码,并且要求编译时和运行时的对象大小是一致的,如果我们主程序中声明了一个在栈上的label对象,编译器在编译时(主程序+1.0库)认为对象的大小是2,而升级库到1.1后,主程序运行时的实际大小为3,那么在创建对象的时候就会把栈中的数据覆盖掉从而破坏栈。
至此我们得出一个结论,如果希望程序在库升级后能继续使用,我们就不能改变类的大小。
D指针
解决方法就是让导出的类拥有一个指针,这个指针指向了所有内部的数据,当内部数据的成员增减时,由于这个指针只在库中用到,因此只会影响到库,对主程序而言,类的大小一直都是一个内部数据指针的大小,因此不会对主程序产生影响,这个指针在Qt中称为D指针。
/* Since d_ptr is a pointer and is never referenced in header file(it would cause a compile error) WidgetPrivate doesn't have to be included,but forward-declared instead.The definition of the class can be written in widget.cpp orin a separate file, say widget_p.h */class WidgetPrivate;class Widget{// ...Rect geometry() const;// ... private:WidgetPrivate *d_ptr;};
在widget_p.h中
/* widget_p.h (_p means private) */
struct WidgetPrivate
{Rect geometry;String stylesheet;
};
widget.cpp
// With this #include, we can access WidgetPrivate.
#include "widget_p.h"Widget::Widget() : d_ptr(new WidgetPrivate)
{// Creation of private data
}Rect Widget::geometry() const
{// The d-ptr is only accessed in the library codereturn d_ptr->geometry;
}
class Label : public Widget
{// ...String text();private:// Each class maintains its own d-pointerLabelPrivate *d_ptr;
};
label.cpp
// Unlike WidgetPrivate, the author decided LabelPrivate
// to be defined in the source file itself
struct LabelPrivate
{String text;
};Label::Label() : d_ptr(new LabelPrivate)
{
}String Label::text()
{return d_ptr->text;
}
由于d指针只在库中使用,而每次发布库都会重新编译,因此Private类可以随意更改而不会影响主程序。
这种实现方式有如下好处:
- 二进制兼容性
- 隐藏实现细节。只需一个头文件和一个库。
- 头文件没有实现细节相关的api,用户可以更清晰的看到能使用的api
- 编译更快。因为所有实现细节都从头文件都移到了实现类的cpp文件中
Q指针
有时在Private实现类中我们希望访问原有类的指针,调用它的一些函数,因此在实现类中通常会保存一个指针指向原有的类,这个指针我们称为Q指针。
widget.h
class WidgetPrivate;class Widget
{// ...Rect geometry() const;// ...
private:WidgetPrivate *d_ptr;
};
widget_p.h
struct WidgetPrivate
{// Constructor that initializes the q-ptrWidgetPrivate(Widget *q) : q_ptr(q) { }Widget *q_ptr; // q-ptr points to the API classRect geometry;String stylesheet;
};
widget.cpp
#include "widget_p.h"
// Create private data.
// Pass the 'this' pointer to initialize the q-ptr
Widget::Widget() : d_ptr(new WidgetPrivate(this))
{
}Rect Widget::geometry() const
{// the d-ptr is only accessed in the library codereturn d_ptr->geometry;
}
label.h
class Label : public Widget
{// ...String text() const;private:LabelPrivate *d_ptr;
};
label.cpp
// Unlike WidgetPrivate, the author decided LabelPrivate
// to be defined in the source file itself
struct LabelPrivate
{LabelPrivate(Label *q) : q_ptr(q) { }Label *q_ptr;String text;
};Label::Label() : d_ptr(new LabelPrivate(this))
{
}String Label::text()
{return d_ptr->text;
}
优化d指针继承
注意到Widget和Label类都声明了一个各自类的Private类指针,且子类构造函数在实例化父类时使用的是默认无参的构造函数,因此,当我们实例化Label时,会先调用基类的构造函数,然后new WidgetPrivate,接着调用子类的构造函数,然后new LabelPrivate,对于某些深度继承的类,这种设计将会造成多次的内存申请,且有多个相互独立的Private类对象存在,子类的d_ptr还会覆盖父类的同名数据成员d_ptr。解决方法是让Private类也具有继承关系,将子类的指针沿着Private类继承链向上传递。注意这种方式要求Private父类要在单独的头文件中声明(而不是直接写在cpp文件中),否则无法被其他Private子类继承。改进后的设计如下:
widget.h
class Widget
{
public:Widget();// ...
protected:// only subclasses may access the below// allow subclasses to initialize with their own concrete PrivateWidget(WidgetPrivate &d);WidgetPrivate *d_ptr;
};
widget_p.h
struct WidgetPrivate
{WidgetPrivate(Widget *q) : q_ptr(q) { } // constructor that initializes the q-ptrWidget *q_ptr; // q-ptr that points to the API classRect geometry;String stylesheet;
};
widget.cpp
Widget::Widget() : d_ptr(new WidgetPrivate(this))
{
}Widget::Widget(WidgetPrivate &d) : d_ptr(&d)
{
}
label.h
class Label : public Widget
{
public:Label();// ...
protected:Label(LabelPrivate &d); // allow Label subclasses to pass on their Private// notice how Label does not have a d_ptr! It just uses Widget's d_ptr.
};
label.cpp
#include "widget_p.h"class LabelPrivate : public WidgetPrivate
{
public:String text;
};Label::Label(): Widget(*new LabelPrivate) // initialize the d-pointer with our own Private
{
}Label::Label(LabelPrivate &d) : Widget(d)
{
}
当我们创建Label对象,只会发生一次内存申请,即new LabelPrivate。
Q_D和Q_Q
在Label类方法中,当我们访问d_ptr时,访问的是基类声明的WidgetPrivate类型的指针,在Label类的方法中为了访问LabelPrivate的成员text,需要向下转换
void Label::setText(const String &text)
{LabelPrivate *d = static_cast<LabelPrivate*>(d_ptr); // cast to our private typed->text = text;
}
Qt定义了Q_D函数帮我们做上述的转化,以简化代码,Q_Q函数类似:
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
其中d_func()是把d_ptr进行类似static_cast<LabelPrivate*>的类型转换。于是代码简化为:
// With Q_D you can use the members of LabelPrivate from Label
void Label::setText(const String &text)
{Q_D(Label);d->text = text;
}// With Q_Q you can use the members of Label from LabelPrivate
void LabelPrivate::someHelperFunction()
{Q_Q(Label);q->selectAll();
}
d_func()通过Q_DECLARE_PRIVATE定义了两个版本,一个返回Private类指针,一个返回const Private类指针,此外Q_DECLARE_PRIVATE还声明ClassPrivate是Class的友元类,如下:
#define Q_DECLARE_PRIVATE(Class)\inline Class##Private* d_func() {\return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));\}\inline const Class##Private* d_func() const {\return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));\}\friend class Class##Private;
我们可以在公共类(导出类)声明一个Q_DECLARE_PRIVATE,从而快速声明返回const和非const Private类指针的d_func(),以及声明Private类是当前类的友元类,如下:
class QLabel
{
private:Q_DECLARE_PRIVATE(QLabel)
};
friend class Class##Private
的作用是让Private类可以访问公共类的public/protected/private接口。
注意,当我们需要使用const版本的Private类指针时,使用如下写法:
Q_D(const Label); // 自动调用const版本的d_func()
通常d_func()是在类内部使用,不过某些情况下,也可以通过声明friend class的方式使得其它类可以访问当前类内部的数据,这些数据通常无法从当前类的公共api得到,例如,在QLabel类中声明ClassA是友元类,ClassA对象访问QLabel内部数据的方法如下:
// ClassA声明为QLabel的friend class, ClassA方法中可以有如下调用
label->d_func()->linkClickCount;
参考:
- https://wiki.qt.io/D-Pointer
相关文章:
Qt-D指针与Q指针的设计哲学
文章目录 前言PIMLP与二进制兼容性D指针Q指针优化d指针继承Q_D和Q_Q 前言 在探索Qt源码的过程中会看到类的成员有一个d指针,d指针类型是一个private的类,这种设计模式称为PIMPL(pointer to implementation),本文根据Q…...
安装配置Anaconda,配置VSCode
文章目录 Anaconda介绍下载Anaconda安装Anaconda换源创建一个新环境conda常用命令 VSCode环境配置 记录一下笔者收集的一些资料,不喜勿喷。 Anaconda介绍 Anaconda是一个用于科学计算的Python发行版,支持 Linux, Mac, Windows系统,提供了包管…...
迪威 3D 模型发布系统:制造业产品展示革新利器
在竞争激烈的制造业领域,如何将产品全方位、直观地呈现给客户,成为企业脱颖而出的关键。传统的产品展示方式往往受限于平面资料或有限的实物展示,难以让客户深入了解产品的复杂结构与精妙细节。迪威 3D 模型发布系统的问世,为制造…...
Matlab 汽车振动多自由度非线性悬挂系统和参数研究
1、内容简介 略 Matlab 169-汽车振动多自由度非线性悬挂系统和参数研究 可以交流、咨询、答疑 2、内容说明 略 第二章 汽车模型建立 2.1 汽车悬架系统概述 2.1.1 悬架系统的结构和功能 2.1.2 悬架分类 2.2 四分之一车辆模型 对于车辆动力学,一般都是研究其悬…...
编程题-第k个语法符号(中等)
题目: 我们构建了一个包含 n 行( 索引从 1 开始 )的表。首先在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。 例如,对于 n 3 ,第 1 行是 0 ,第 2 行是 01 ,第3行…...
vscode打不开
Bug:窗口意外终止(原因:“launch-failed”,代码:“65" 对此造成的不便,我们深表歉意。可以打开新的空窗口以重新启动) 解决方法: 移情别恋:VS Code打开闪退并报‘launch-failed‘ code:‘65‘ 窗口意外终止(原因…...
使用生成对抗网络(GAN)进行人脸老化生成的Python示例
以下是一个使用生成对抗网络(GAN)进行人脸老化生成的Python示例,我们将使用PyTorch库来实现。GAN由生成器和判别器两部分组成,生成器尝试生成逼真的老化人脸图像,判别器则尝试区分生成的图像和真实的老化人脸图像。 步…...
202503执行jmeter压测数据库(ScyllaDB,redis,lindorm,Mysql)
一、Mysql 1 、 准备MySQL 连接内容 2 、 下载连接jar包 准备 mysql-connector-java-5.1.49.jar 放到 D:\apache-jmeter-5.6.3\lib\ext 目录下面; 3 、 启动jmeter ,配置脚本 添加线程组---》JDBC Connection Configuration---》JDBC Request---》查看结果树。 1)测…...
Uniapp当中的scroll-view滚动条不出现或者触底刷新事件不触发
一、未正确设置容器高度 问题描述 scroll-view 未设置明确高度或高度值无效,导致无法形成有效滚动区域。 解决方案 • 使用行内样式直接设置 height(如 style"height: 500rpx;"),避免类名样式被覆盖。 • 动态计算高度…...
3.16学习总结
学习了Java的知识点 基本数据类型 byte占1字节,储存范围-128~127 short占2字节,储存范围-32768~32767 int占4字节,储存范围-2147483648~2147483647 long占8字节,储存范围是-9223372036854775808~9223372036854775807 float占…...
206. 反转链表
目录 一、题目二、思路2.1 解题思路2.2 代码尝试2.3 疑难问题2.4 AI复盘 三、解法四、收获4.1 心得4.2 举一反三 一、题目 二、思路 2.1 解题思路 需要有头尾指针,然后又觉得可以用递归 2.2 代码尝试 class Solution { public:ListNode* reverseList(ListNode* h…...
音视频缓存数学模型
2024年8月的笔记 音视频缓存数学模型 - Wesley’s Blog 播放器作为消费者,缓存作为生产者。 进入缓冲一次 设消费者速率为v1,生产者为v2,视频长度为l,x为生产者至少距离消费者多远才能保证在播完视频前两者重合。实际上就是一个…...
priority_queue模拟实现
目录 介绍 模拟实现 无参构造函数 迭代器构造 push插入 pop删除 top返回队首元素 返回队列元素个数 判空 仿函数 补充:反向迭代器 模拟实现反向迭代器 构造和析构 重载和-- 解引用*和地址访问-> ! 反向迭代器在容器中实现 补充练习 数组中第…...
ChatGPT、DeepSeek、Grok:AI 语言模型的差异与应用场景分析
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 1. 引言 人工智能(AI)语言模型正在快速发展,ChatGPT(OpenAI)、DeepSe…...
Nginx 刷新后 404 的原因与解决方案
文章目录 Nginx 刷新后 404 的原因与解决方案静态资源路径问题(单页应用 SPA)解决方案:使用 try_files Nginx 资源路径 (root 或 alias) 配置错误示例:正确的 root配置 浏览器缓存或 Nginx 缓存影响清除浏览器缓存给静态资源加版本…...
AI赋能实时安全背带监测解决方案
背景:安全背带检测的行业刚需与技术痛点 在建筑施工、石油化工、仓储物流等高危行业中,安全背带是保障作业人员生命安全的最后一道防线。据统计,超过30%的高空坠落事故与未正确佩戴安全背带直接相关。传统依赖人工巡检的监督方式存在效率低、…...
【ES6】03-Set + Map
本文介绍两种集合 set map 的操作和方法。 目录 1. Set 1.1 set基本使用 1.2 add 1.3 delete 1.4 has 1.5 size 1.6 set转换为数组 1.7 拓展运算符 1.8 for...of 1.9 forEach 1.10 set给数组去重 2. Map 2.1 创建map集合 2.2 set添加元素 2.3 delete删除元素 …...
GHCTF web方向题解
upload?SSTI! import os import refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirect from werkzeug.utils import secure_filename import os from werkzeug.utils import secure_filenameapp Flask(__name__)# 配置…...
网页制作15-Javascipt时间特效の记录网页停留时间
01效果图: 02运用: window.setTimeout()刷新function()函数document.forms():表单if条件语句window.alert()窗口警示 03、操作代码:…...
LeetCode 解题思路 15(Hot 100)
解题思路: 引入哑节点: 简化头节点删除操作,统一处理所有边界条件。快慢指针法: 快指针先移动 n 步,确保快慢指针距离为 n,之后同步移动快慢指针。当快指针到达末尾时,慢指针指向倒数第 n 个节…...
STM32 - 在机器人领域,LL库相比HAL优势明显
在机器人控制器、电机控制器等领域的开发,需要高实时性、精细化控制或者对代码执行效率、占用空间有较高要求。所以,大家常用的HAL库明显不符合要求。再加上,我们学习一门技术,一定要学会掌握底层的原理。MCU开发的底层就是寄存器…...
如何查看mysql某个表占用的空间大小
在MySQL中,有几种方法可以查看某个表占用的空间大小。这通常涉及到查询数据库的元数据表,如 information_schema.TABLES,或者使用特定于存储引擎的命令(例如对于InnoDB引擎)。以下是几种常用的方法: 方法一…...
加固脱壳技术:DEX动态加载对抗
1. 加固技术原理剖析 1.1 DEX保护演进路线 加固方案发展历程: graph LR A[2015 代码混淆] --> B[2017 DEX动态加载] B --> C[2019 VMP指令虚拟化] C --> D[2022 全链路加密] 1.1.1 主流加固方案对比 厂商核心防护技术弱点分析梆梆加固DEX文件分片…...
树莓派 连接 PlutoSDR 教程
在树莓派5上安装PlutoSDR(ADALM-Pluto)的驱动程序,主要需要安装相关的库和工具,以便与PlutoSDR通信,比如libiio和libad9361,并确保系统能够识别设备。由于树莓派5运行的是基于Linux的系统(通常是…...
山东省新一代信息技术创新应用大赛-计算机网络管理赛项(样题)
目录 竞赛试题 网络拓扑 配置需求 虚拟局域网 IPv4地址部署 OSPF及路由部署 配置合适的静态路由组网 MSTP及VRRP链路聚合部署 IPSEC部署 路由选路部署 设备与网络管理部署 1.R1 2.R2 3.S1 4.S2 5.S3 竞赛试题 本竞赛使用HCL(华三云实验室)来进行网络设备选择…...
文件上传漏洞
文件上传条件 有上传点 后缀没有过滤且可解析 能找到web路径 常见的绕过方式 前端绕过:上传一张图片,抓包修改后缀为php content-type绕过: applicaition/x-www-form-urlencoded multipart/form-data application/json image/jpeg image/png…...
Java 绘制图形验证码
在 Spring Boot 中生成图形验证码并校验其正确性,通常包括以下步骤: 生成验证码图片和对应的验证码值。将验证码值存储到 Session 或其他存储中(如 Redis)。将验证码图片返回给客户端。客户端提交表单时,校验用户输入的…...
APP自动化测试-备忘录:Appium 2.X的安装和启动服务方法
一、Appium 1.X 启动appium服务:appium --session-override 也可以这样启动appium服务:appium 二、Appium 2.X Appium 2.X 是一个自动化测试开源工具,用于测试原生、移动 Web 和混合应用程序。下面为你介绍其下载步骤: 1. 安装…...
卡尔曼滤波算法从理论到实践:在STM32中的嵌入式实现
摘要:卡尔曼滤波(Kalman Filter)是传感器数据融合领域的经典算法,在姿态解算、导航定位等嵌入式场景中广泛应用。本文将从公式推导、代码实现、参数调试三个维度深入解析卡尔曼滤波,并给出基于STM32硬件的完整工程案例…...
k8s serviceaccount在集群内指定apiserver时验证错误的问题
在主机上,找到TOKEN,可以直接指定apiserver使用 rootubuntu-server:/home# kubectl auth can-i --list --server https://192.168.85.198:6443 --token"eyJhbGciOiJSUzI1NiIsImtpZCI6IlFlMHQ3TzhpcGw1SnRqbkYtOC1NUWlWNUpWdGo5SGRXeTBvZU9ib25iZD…...
自动化测试脚本
一、自动化测试脚本是什么? 自动化测试脚本是由测试人员或开发人员编写的 代码或工具指令,用于 自动执行测试用例,模拟用户操作(如点击按钮、输入数据),并对结果进行验证。 它与手动测试的核心区别在于&am…...
练习-串串变变变(字符串变换)
问题描述 小蓝现在有一个长度为 n 仅由小写字母组成的的字符串 s ,小蓝可以对字符串进行任意次操作,每次操作小蓝可以选择一个整数 ii ,其中 i∈[1,n−1],然后选择如下两种操作之一: 将 si 变为其字典序加一的小写…...
99.HarmonyOS NEXT跑马灯组件教程:动画配置与参数详解
温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦! HarmonyOS NEXT跑马灯组件教程:动画配置与参数详解 文章目录 HarmonyOS NEXT跑马灯组件教程:动画配置与参数详解1. 跑马灯动…...
【web逆向】优某愿 字体混淆
地址:aHR0cHM6Ly93d3cueW91enkuY24vY29sbGVnZXMvc2NvcmVsaW5lP2NvbGxlZ2VDb2RlPTEwMDAzJm5hbWU9JUU2JUI4JTg1JUU1JThEJThFJUU1JUE0JUE3JUU1JUFEJUE2 接口分析 接口:eW91enkuZG1zLmRhdGFsaWIuYXBpLmVucm9sbGRhdGEuZW50ZXIuY29sbGVnZS5lbmNyeXB0ZWQudj…...
MSP430 Proteus 仿真作品
https://www.dong-blog.fun/post/1998 1 、 电子万年历(采用 DS1302 及 及 TC72 等芯片) 基本要求: 可显示年、月、日、星期、时、分、秒; 有温度显示功能。 发挥部分: 可调节时间和日期; 有农历显示功能 &…...
八股文——C 语言宏、`volatile`、`static`、动态内存管理、堆与栈的区别
文章目录 1. #(字符串化操作符)作用:示例: 2. ##(符号连接操作符)作用:示例1:动态生成变量名 3. volatile 关键字作用:示例: 4. static 关键字作用࿱…...
创新实践分享:基于边缘智能+扣子的智能取物机器人解决方案
在 2024 年全国大学生物联网设计竞赛中,火山引擎作为支持企业,不仅参与了赛道的命题设计,还为参赛队伍提供了相关的硬件和软件支持。以边缘智能和扣子的联合应用为核心,参赛者们在这场竞赛中展现出了卓越的创新性和实用性…...
【QT笔记---QText】
文章目录 概要1、字体样式设置1.1效果1.2demo1.3常用成员函数 概要 QText基本应用:1、字体样式设计; 1、字体样式设置 1.1效果 1.2demo //若需要设置字体、字体大小、字宽或者斜体状态的话,可以直接初始化时一起设置 // QFont::QFont(cons…...
Html label标签中的for属性(关联表单控件:将标签与特定的表单元素(如输入框、复选框等)关联起来;提高可用性;无障碍性)
文章目录 示例代码for属性含义完整代码示例 示例代码 <div class"form-group"> <!-- 表单组,包含省份输入框和标签 --><label for"province">省份名称:</label> <!-- 省份输入框的标签 --><input…...
二叉树的基本操作与实现:C语言深度剖析
目录 代码整体框架 1. #define _CRT_SECURE_NO_WARNINGS 2. 头文件引入 3. typedef int BTtype; 4. 二叉树节点结构体定义 二叉树的创建 1. BuyNode 函数 2. CreatNode 函数 二叉树的遍历 前序遍历 中序遍历 后序遍历 二叉树属性的计算 节点个…...
【人工智能】Deepseek 与 Kimi 联袂:重塑 PPT 创作,开启智能演示新纪元
我的个人主页 我的专栏:人工智能领域、java-数据结构、Javase、C语言,希望能帮助到大家!!!点赞👍收藏❤ 前言 在当今快节奏的工作与学习场景中,PPT 制作常常是一项耗时耗力的任务。从前期的资…...
Nest系列:NestJS 中 Logger 完全指南:从基础到企业级实践-04
一、Logger 的核心价值 在服务端应用中,日志系统承担着三大核心职责: 系统监控:实时反馈应用健康状态问题追踪:快速定位异常根源行为审计:记录关键业务操作NestJS 内置的日志系统提供了开箱即用的解决方案,支持: ✅ 多日志级别管理 ✅ 上下文感知日志 ✅ 自定义输出格式…...
行为模式---模版模式
概念 模版模式是设计模式行为模式的一种,它的核心思想是定义一个算法骨架,将某些步骤提取到到子类中实现。解决当项目中出现多个处理分支,这几个处理分支有重复步骤的时实现代码的复用和扩展。在这种模式下不用修改逻辑结构,使用…...
C++博客分享
本周的一些 C视频分享, 或许后续会做一些内容总结. 博客 Polymorphic, Defaulted EqualityConstexpr factors_ofC26: Removing language featuresBypassing the branch predictor Meeting C 2024 Clean CMake for C (library) developers - Kerstin KellerAn Introduction …...
Cesium 入门教程(基于 vue3)
目录 Cesium 介绍: 下载 Cesium,2种路径: 下载成功后,创建 vue3 项目: 编写内容 一个“纯”地球 添加图层 坐标系及其数值转换 相机位置及动态交互 添加物体和3维建筑物 Cesium 介绍: Cesium 是一个开源的 JavaScript …...
Power Apps 技术分享:连接SharePoint列表数据源
前言 在使用Power Apps的时候,使用列表作为数据源是非常方便和经济的,列表创建简单,SharePoint的存储也不像Dataverse需要按照容量付费。 正文 1.我们先在SharePoint中建一个列表,添加一些测试数据,如下图:…...
Flutter FloatingActionButton 从核心用法到高级定制
目录 1. 引言 2. FloatingActionButton 的基本用法 3. 主要属性 4. 进阶定制技巧 4.1 扩展型 FAB 4.2 动态变形动画 4.3 多个 FAB 协同 5. 主题与动效集成 5.1 全局主题配置 5.2 平台适配方案 5.3 高级动画控制器 6. 最佳实践 6.1 布局规范 6.2 性能优化 6.3 无…...
42、【OS】【Nuttx】【OSTest】内存监控:堆空间初始化
背景 接上篇blog 41、【OS】【Nuttx】【OSTest】内存监控:堆空间申请 分析了堆空间的申请,下面分析堆管理器如何初始化申请后的堆空间 用户堆空间初始化 回到 umm_initialize 函数,之前 blog 40、【OS】【Nuttx】【OSTest】内存监控&#…...
STM32---FreeRTOS消息队列
一、简介 1、队列简介: 队列:是任务到任务,任务到中断、中断到任务数据交流的一种机制(消息传递)。 FreeRTOS基于队列,实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量…...
Web前端开发——CSS入门
CSS入门 一、CSS是什么?二、CSS的基本语法三、如何使用CSS1. 内联样式2. 内部样式表3. 外部样式表 四、CSS选择器1. 元素选择器2. 类选择器3. ID选择器 五、CSS的常用属性1. 文本样式2. 布局3. 背景 一、CSS是什么? CSS(Cascading Style She…...