QtC++ 技术分析4 - 流、d-pointer隐式共享以及容器迭代器

news/2024/5/19 8:51:09

目录

    • QT 中的流
      • 文件系统与底层文件操作
        • 文件系统
        • 类 QFile
      • QTextStream
      • QDataStream
      • QLocale
    • 隐式共享与 d-pointer
      • 隐式共享
      • d-pointer 在隐式共享中的应用
      • 二进制代码兼容
      • d-pointer 模式实现
    • Qt 容器及迭代器
      • QTL 概述
        • 几种常见的迭代器及其对应类型
        • QTL 容器对应迭代器
        • 通用算法
        • 函子(谓词)
      • QTL 容器与 QDataStream
      • 类型分类计数在 QList 中的应用

QT 中的流

下图展示 QT 中流的框架,含两个核心类 QTextStream 以及 QDataStream
在这里插入图片描述

QTextStream 对数据进行文本格式的输入/输出
QDataStream 对数据进行二进制格式的输入/输出
QFile ,负责文件的处理。
QTemporaryFile 创建并访问临时文件。
QBuffer 负责以 QIODevice 的接口访问一个 QByteArray 对象。
QProcess 负责以进程的形式启动一个外部程序,并和该进程进行通信。
QTcpSocket 与 QUdpSocket 负责使用 TCP、UDP 协议进行网络数据的发送和接收。

QTextCodec 负责 Unicode 与各种字符编码方式之间的转换。
QLocale 负责实现各种区域文化。


文件系统与底层文件操作

UNIX 系统中路径分割符为“/”,而 Windows 系统的为“\”

文件系统

Qt 使用类 QFileInfo 表示一个目录项的属性,类名中的“File”并不仅仅表示文件,而是泛指所有类型的目录项。

类 QDir 刻画一个目录的详细信息。
成员函数 entryInfoList(),返回该目录下子目录、文件以及链接的信息。


类 QFile

无论对于什么操作系统,子目录之间的分隔符都应该为“/”

//包含Qt框架中的QFile和QDebug库
#include <QFile>
#include <QDebug>int main()
{//定义一个QFile对象f,并指定要打开的文件路径QFile f("data/test.txt");//以只读文本模式打开文件,如果打开失败,则返回-1,表示程序发生错误if (!f.open(QIODevice::ReadOnly | QIODevice::Text))return -1;//将文件中的所有内容读取到一个QByteArray对象中QByteArray data = f.readAll();//输出QByteArray对象data的内容到控制台qDebug() << data;return 0;
}

QTextStream

QTextStream 在其内部使用两个字节长的 QChar 类型存放每个字符,使用 Unicode 编码方式

使用 QTextCodec 进行编码转换

#include <QFile> //包含Qt框架中的QFile库
#include <QTextStream> //包含Qt框架中的QTextStream库int main(int argc, char *argv[])
{//定义一个QFile对象src_f,并指定要打开的文件路径QFile src_f("data/latin1.txt");//以只读模式打开文件,如果打开失败,则返回-1,表示程序发生错误if (!src_f.open(QIODevice::ReadOnly))return -1;//创建一个QTextStream对象in,并将其与QFile对象src_f关联QTextStream in(&src_f);//设置QTextStream对象in的编码格式为Latin1in.setCodec("latin1");//将文件中的所有内容读取到一个QString对象data中QString data = in.readAll();//定义一个QFile对象dest_f,并指定要打开的文件路径QFile dest_f("data/unicode.txt");//以只写模式打开文件,如果打开失败,则返回-1,表示程序发生错误if (!dest_f.open(QIODevice::WriteOnly))return -1;//创建一个QTextStream对象out,并将其与QFile对象dest_f关联QTextStream out(&dest_f);//设置QTextStream对象out的编码格式为UTF-16out.setCodec("UTF-16");//将QString对象data中的内容写入到文件中out << data;return 0;
}

QDataStream

QDataStream 可以处理自定义类型

用户 struct 定义新的结构体,并通过运算符重载的方式,搭配友元实现

struct ColorText{QString text;QColor  color;
};
QDataStream& operator << (QDataStream & stream, const ColorText & data)
{stream << data.text << data.color;return stream;
}
QDataStream& operator >> (QDataStream & stream, ColorText & data)
{stream >> data.text >> data.color;return stream;
}
int main()
{ColorText data;data.text  = "Red";  data.color = Qt::red;QFile file( "test.dat" );if( !file.open( QIODevice::ReadWrite) )  return -1;QDataStream stream( &file );stream << data;file.seek(0);   stream >> data;file.close();qDebug() << data.text << " " << data.color;
}

QLocale

QLocale 为每种区域文化定义了统一的名字,这个名字不会随着操作系统、编译器平台的变化而变化

QLocale 的静态成员函数 system()返回这个区域文化。
Qt 应用程序本身会有一个默认的区域文化,构造函数 QLocale()返回的就是后者。起初,程序默认的区域文化被设

QTextStream out( stdout, QIODevice::WriteOnly);
int main( )
{double x = 123.456;out.setLocale( QLocale(QLocale::German) );       ①out << fixed << x << endl;
}

隐式共享与 d-pointer

隐式共享定义:一个类的多个对象所占用的内存是相互独立的。如果其中某些对象数据成员的取值完全相同,我们可以令它们共享一块内存以节省空间。只有当程序需要修改其中某个对象的数据成员时,我们再为该对象分配新的内存。

d-pointer:把与主类密切相关的数据成员抽离作为一个私类,主类中再定义一个指针指向该私类


隐式共享

QString 中的成员函数 toCaseFolded() 就用到了隐式共享技术
它使用引用计数的方式判断字符串是否相同,如果相同,则共享一块内存地址


d-pointer 在隐式共享中的应用

Qt 常在主类的名字后面加上后缀“Private”或者“Data”形成从类的名字

为了能够共享数据,我们必须将类中的数据分离出来,定义在一个单独的类中,再定义一个指针指向这个新类。
这个指针就被称为 d-pointer,这个模式就被称为 d-pointer 模式。
包含有 d-pointer 的那个类被称为主类,d-pointer 所指的那个类被称为从类。


二进制代码兼容

采用动态链接方式时,客户只需要更新 Qt 的动态链接库,不需要重新编译、部署 Qt 应用程序。
如果在这种情形下这些 Qt 应用程序仍然能够正常工作,我们称这个 动态链接库是二进制兼容的

程序员不可以添加、删除非静态数据成员,不可以更改非静态数据成员的定义顺序、类型

二进制兼容性通常包括以下三个方面

  1. ABI 兼容性:ABI(Application Binary Interface)指的是二进制接口,即不同编译器生成的二进制代码之间的接口规范。如果两个编译器使用相同的 ABI 规范,那么它们生成的二进制代码就可以在不同的平台上互相使用,这就是 ABI 兼容性。
  2. 数据类型兼容性:C++ 的数据类型在不同编译器、不同版本或不同操作系统上可能会有不同的大小和布局,如果不同的编译器使用相同的数据类型布局,就可以实现数据类型兼容性。
  3. 二进制格式兼容性:不同的操作系统和平台可能使用不同的二进制格式来存储可执行文件和库文件,如果不同的平台使用相同的二进制格式,就可以实现二进制格式兼容性。

d-pointer 模式实现

D-pointer 模式用于实现类的私有数据封装和二进制接口兼容性

实现 d-pointer 模式的主要步骤:

  1. 定义一个包含类的私有数据的结构体,并将其作为类的成员变量。
  2. 将类的所有公共成员函数的实现都移动到类的实现文件中,并在实现文件中定义一个指向私有数据结构体的指针。这个指针可以使用 new 运算符在堆上分配内存,也可以使用 std::unique_ptr 或 std::shared_ptr 等智能指针管理内存。
  3. 在类的构造函数中,为 D-指针分配内存,并将其指向私有数据结构体。在析构函数中,释放 D-指针的内存。
  4. 将类的公共接口重定向到私有数据结构体中的成员函数。这可以通过在公共接口中使用 D-指针来实现。
  5. 在类的头文件中,只声明类的公共接口,而不暴露私有数据结构体或 D-指针。

Qt 容器及迭代器

为便于跨平台,QT 研发出了 QTL(类似于 Cpp 的 STL),但是其运行速度较慢


QTL 概述

几种常见的迭代器及其对应类型

几种必备容器(单类型容器)

  • QVector<T> 将其所有元素存放在一块连续的内存中。随机访问的速度很快,但是插入/删除操作很慢。
  • QStack<T>QVector<T>的子类,实现栈的功能
  • QList<T> 在内部使用一个指针数组指向容器元素。能够快速随机访问每个元素。在容器首、尾添加元素的速度也较快。
  • QStringListQList<QString> 的子类,能高效地处理字符串列表。QQueue<T>QList<T>的子类,实现了队列的功能。
  • QLinkedList<T> 能够在很短而且固定的时间内完成元素的插入/删除操作,但是排序、查找操作却较慢。

键值对类型容器

  • QMultiMap<Key, T>
    它是 QMap 的子类,其 insert 函数允许新元素的 key 和已有元素的 key 相同。
    它不支持运算符“[]”,取而代之的是函数 values(),该函数返回所有具有指定 key 值的元素,并将它们存放在一个 QList 对象中。
  • QHash<Key, T>
    使用哈希表存取 key,因而能够快速地依据 key 定位某个元素
    元素并没有按照 key 的取值排序,降低了搜索的性能
  • QSet<T>
    内部使用 QHash 实现集合的功能
    能够快速完成集合的插入、元素定位操作。
    unite()合并两个集合,intersect()求取两个集合的交集,substract()求取两个集合之差,contains()判断一个集合是否含有某个元素。
  • QCache<Key,T>
    依然也为键值对的存储形式
    QCache 所能存放的元素数量被有意地限定
    当有新元素需要被插入到容器中时,最近使用频率最低的那些元素会被删除。

QTL 容器对应迭代器

foreach 遍历容器的时候,接收的第一个参数表示 foreach 得到的元素,这里必须使用 typedef 预定义类型,然后直接使用

就如下方代码的 foreach( pair_type element, list),你不可以把 pair_type 更换为 QPair<string,double>

typedef QPair<string,double> pair_type;
QList< pair_type > list;
list << pair_type("pi",3.14) << pair_type("e", 2.718);foreach( pair_type element, list)②cout << element.first  << " " << element.second << endl;

通用算法

只要一个容器内部的迭代器支持这些基本操作,该函数模板就可以操作这个容器。这样的函数模板被称为 通用算法(algorithm)

通用算法 qSort 使用快速排序算法,将一个元素序列排成升序
对于有序容器,通用算法 qBinaryFind 使用二分搜索算法
qUpperBound 在一个元素序列中寻找最后一个大于指定值的元素,返回指向该元素的一个迭代器
无论容器是否有序,qFind 在容器中逐个搜索与某个指定值相等的元素,并返回一个指向该元素的迭代器。

qCount 计算某个值在容器中出现的次数。
qDeleteAll 调用 C++运算符 delete,析构容器中的元素。
qEqual 比较两个元素序列是否相等。
qSwap 调换两个元素的值。


函子(谓词)

QTL 提供了类似于 CPP 中的“谓词”操作

即类似于 CPP 中的 sort 函数,最后一个参数为控制排序方式的匿名回调函数

下图即使用了 QT 自己提供的成型谓词 qLess 来进行降序排列

QList<int> ql;
ql << 4 << 3 << 2 << 1 << 0;
qSort(ql.begin(),  ql.end(),  qLess<int>() );

QTL 容器与 QDataStream

一段代码,自己体会,不去解释,学学就懂

/*** @brief 保存词典到文件中** @param dict 词典对象的引用* @param fname 要保存的文件名* @return int 如果保存成功返回 0,否则返回 -1*/
int save_dict_map(dict_type& dict, char* fname)
{QFile dictf(fname);   // 创建一个 QFile 对象,并指定文件名if (!dictf.open(QIODevice::WriteOnly)) return -1;  // 尝试以只写方式打开文件,如果失败则返回 -1QDataStream ds(&dictf);  // 创建一个 QDataStream 对象,并将其与 QFile 关联ds << dict;  // 将词典对象保存到 QDataStream 中cout << fname << " saved\n";  // 输出保存成功的消息
}/*** @brief 从文件中加载词典** @param dict 词典对象的引用* @param fname 要加载的文件名* @return int 如果加载成功返回 0,否则返回 -1*/
int load_dict_map(dict_type& dict, char* fname)
{QFile dictf(fname);  // 创建一个 QFile 对象,并指定文件名if (!dictf.open(QIODevice::ReadOnly)) return -1;  // 尝试以只读方式打开文件,如果失败则返回 -1QDataStream ds(&dictf);  // 创建一个 QDataStream 对象,并将其与 QFile 关联ds >> dict;  // 从 QDataStream 中加载词典对象cout << fname << " loaded\n";  // 输出加载成功的消息
}

类型分类计数在 QList 中的应用

容器元素的类型被表示为 QList 的模板参数 T
如果 T 占用的内存较多,QList 将每个容器元素存放在堆中,再维护一个指针数组,指向这些元素

对 QList 的插入或者删除操作会比单向链表的要稍慢,因为需要使用标准函数 memmove() 来移动数组中的指针



http://www.mrgr.cn/p/08673314

相关文章

超全整理,Jmeter性能测试-常用Jmeter第三方插件详解(超细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Jmeter作为一个开…

批量插入数据、MVC三层分离

八、批量插入数据 1、使用Statement&#xff08;&#xff09; 2、使用PreparedStatement() 3、使用批量操作API 4、优化 九、MVC三层分离

Windows下安装HBase

Windows下安装HBase 一、HBase简介二、HBase下载安装包三、环境准备3.1、 JDK的安装3.2、 Hadoop的安装 四、HBase安装4.1、压缩包解压为文件夹4.2、配置环境变量4.3、%HBASE_HOME%目录下新建临时文件夹4.4、修改配置文件 hbase-env.cmd4.4.1、配置JAVA环境4.4.2、set HBASE_MA…

高等数学中如何求间断点

高等数学中求间断点是一项重要的技巧&#xff0c;特别适用于分析函数的性质和图像的特征。在本文中&#xff0c;我们将深入探讨如何在给定函数中找到间断点&#xff0c;并解释其数学原理和实际应用。 什么是间断点&#xff1f; 在高等数学中&#xff0c;间断点是指函数在某个点…

加利福尼亚大学|3D-LLM:将3D世界于大规模语言模型结合

来自加利福尼亚大学的3D-LLM项目团队提到&#xff1a;大型语言模型 (LLM) 和视觉语言模型 (VLM) 已被证明在多项任务上表现出色&#xff0c;例如常识推理。尽管这些模型非常强大&#xff0c;但它们并不以 3D 物理世界为基础&#xff0c;而 3D 物理世界涉及更丰富的概念&#xf…

windows下载安装FFmpeg

FFmpeg是一款强大的音视频处理软件&#xff0c;下面介绍如何在windows下下载安装FFmpeg 下载 进入官网: https://ffmpeg.org/download.html, 选择Windows, 然后选择"Windows builds from gyan.dev" 在弹出的界面中找到release builds, 然后选择一个版本&#xff0…

亚马逊云科技全新Amazon Bedrock,助力客户构建生成式AI应用

亚马逊云科技近日在纽约峰会上宣布全面扩展其全托管基础模型服务Amazon Bedrock&#xff0c;包括新增Cohere作为基础模型供应商&#xff0c;加入Anthropic和Stability AI的最新基础模型&#xff0c;并发布变革性的新功能Amazon Bedrock Agents功能。客户无需管理任何基础设施&a…

Jenkins 安装构建

一、CentOS 安装 1. 使用该存储库 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key 2. 安装 Java yum install fontconfig java-11-openjdk配…

java实现文件下载

1.文件上传 文件上传&#xff0c;也称为upload&#xff0c;是指将本地图片、视频、音频等文件上传到服务器上&#xff0c;可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛&#xff0c;我们经常发微博、发微信朋友圈都用到了文件上传功能。 import com.itheima.…

前端Web实战:从零打造一个类Visio的流程图拓扑图绘图工具

前言 大家好&#xff0c;本系列从Web前端实战的角度&#xff0c;给大家分享介绍如何从零打造一个自己专属的绘图工具&#xff0c;实现流程图、拓扑图、脑图等类Visio的绘图工具。 你将收获 免费好用、专属自己的绘图工具前端项目实战学习如何从0搭建一个前端项目等基础框架项…

spring6——容器

文章目录 容器&#xff1a;IocIoc容器控制反转&#xff08;Ioc&#xff09;依赖注入IoC容器在Spring的实现 基于XML管理Bean搭建环境获取bean依赖注入setter注入构造器注入特殊值处理字面量赋值null值xml实体CDATA节 特殊类型属性注入为对象类型属性赋值方式一&#xff1a;引入…

音频开发-小程序和H5

微信录音 1、引入sdk 2、录音操作 浏览器录音 参考文献&#xff1a;前端H5实现调用麦克风&#xff0c;录音功能_h5 录音_Darker丨峰神的博客-CSDN博客 function record() { window.navigator.mediaDevices.getUserMedia({ audio: { sampleRate: 44100, // 采样率 channelCount…

【软件安装】MATLAB_R2021b for mac 安装

Mac matlab_r2021b 安装 下载链接&#xff1a;百度网盘 下载链接中所有文件备用。 我所使用的电脑配置&#xff1a; Macbook Pro M1 Pro 16512 系统 macOS 13.5 安装步骤 前置准备 无此选项者&#xff0c;自行百度 “mac 任何来源”。 1 下载好「MATLAB R2021b」安装文…

Leetcode-每日一题【剑指 Offer 56 - I. 数组中数字出现的次数】

题目 一个整型数组 nums 里除两个数字之外&#xff0c;其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n)&#xff0c;空间复杂度是O(1)。 示例 1&#xff1a; 输入&#xff1a;nums [4,1,4,6]输出&#xff1a;[1,6] 或 [6,1] 示例 2&#x…

计算机网络——传输层

文章目录 **1 传输层提供的服务****1.1 传输层的功能****1.2 传输层的寻址与端口** **2 UDP协议****2.1 UDP数据报****2.2 UDP校验** **3 TCP协议****3.1 TCP协议的特点****3.2 TCP报文段****3.3 TCP连接管理****3.4 TCP可靠传输****3.5 TCP流量控制****3.6 TCP拥塞控制** 1 传…

Verilog语法学习——LV4_移位运算与乘法

LV4_移位运算与乘法 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 已知d为一个8位数&#xff0c;请在每个时钟周期分别输出该数乘1/…

Spring Security 构建基于 JWT 的登录认证

一言以蔽之&#xff0c;JWT 可以携带非敏感信息&#xff0c;并具有不可篡改性。可以通过验证是否被篡改&#xff0c;以及读取信息内容&#xff0c;完成网络认证的三个问题&#xff1a;“你是谁”、“你有哪些权限”、“是不是冒充的”。 为了安全&#xff0c;使用它需要采用 …

HTTP、HTTPS协议详解

文章目录 HTTP是什么报文结构请求头部响应头部 工作原理用户点击一个URL链接后&#xff0c;浏览器和web服务器会执行什么http的版本持久连接和非持久连接无状态与有状态Cookie和Sessionhttp方法&#xff1a;get和post的区别 状态码 HTTPS是什么ssl如何搞到证书nginx中的部署 加…

2023 蓝桥杯真题B组 C/C++

https://www.dotcpp.com/oj/train/1089/ 题目 3150: 蓝桥杯2023年第十四届省赛真题-冶炼金属 题目描述 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V&#xff0c;V 是一个正整数&#xff0c;这意味着消耗 V 个普通金 属 O…

react-native 输入框 被软键盘遮挡 (KeyboardAvoidingView)

本组件用于解决一个常见的尴尬问题&#xff1a;手机上弹出的键盘常常会挡住当前的视图。本组件可以自动根据键盘的高度&#xff0c;调整自身的 height 或底部的 padding&#xff0c;以避免被遮挡。 <KeyboardAvoidingViewbehavior{Platform.OS ios ? padding : height}key…