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

news2024/11/16 19:37:15

目录

    • 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的编码格式为Latin1
    in.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-16
    out.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;  // 尝试以只写方式打开文件,如果失败则返回 -1
    QDataStream 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;  // 尝试以只读方式打开文件,如果失败则返回 -1
    QDataStream ds(&dictf);  // 创建一个 QDataStream 对象,并将其与 QFile 关联
    ds >> dict;  // 从 QDataStream 中加载词典对象
    cout << fname << " loaded\n";  // 输出加载成功的消息
}

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

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

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/814741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

特性快闪:使用 Databend 玩转 Iceberg

作者&#xff1a;尚卓燃&#xff08;PsiACE&#xff09;澳门科技大学在读硕士&#xff0c;Databend 研发工程师实习生 Apache OpenDAL(Incubating) Committer https://github.com/PsiACE 几周前&#xff0c;Databricks 和 Snowflake 召开了各自的年度大会&#xff0c;除了今年一…

线程池的使用、工作原理和优势

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、线程池概览2.1 为什么创建和销毁线程开销较大2.2 为什…

tinkerCAD案例:26. Making the Amplifier Body 制作放大器主体(1)

tinkerCAD案例&#xff1a;26. Making the Amplifier Body 制作放大器主体 Project Overview: 项目概况&#xff1a; Music is the universal language! And who doesn’t love jamming out to some sweet tunes with friends? But it’s such a bummer when there are more…

【腾讯云Cloud Studio实战训练营】Cloud Studio 快速搭建学习分享

文章目录 零、前言一、Cloud Studio1.1、Cloud Studio是什么1.2、Cloud Studio的优势 二、实战&#xff1a;快速构建React完成点餐H5页面还原2.1、打开官网2.2、选择React 框架模板2.3、编码部分安装 antd-mobile安装 Less安装 normalize上传项目需要的素材替换App.js主文件 2.…

ChatGPT结合知识图谱构建医疗问答应用 (二) - 构建问答流程

一、ChatGPT结合知识图谱 上篇文章对医疗数据集进行了整理&#xff0c;并写入了知识图谱中&#xff0c;本篇文章将结合 ChatGPT 构建基于知识图谱的问答应用。 下面是上篇文章的地址&#xff1a; ChatGPT结合知识图谱构建医疗问答应用 (一) - 构建知识图谱 这里实现问答的流程…

无涯教程-jQuery - Tabs组件函数

窗口小部件选项卡函数可与JqueryUI中的窗口小部件一起使用。选项卡用于在分成逻辑部分的内容之间交换。 Tabs - 语法 $( "#tabs" ).tabs(); Tabs - 示例 以下是显示Tab用法的简单示例- <!doctype html> <html lang"en"><head><m…

选择排序算法

选择排序 算法说明与代码实现&#xff1a; 以下是使用Go语言实现的选择排序算法示例代码&#xff1a; package mainimport "fmt"func selectionSort(arr []int) {n : len(arr)for i : 0; i < n-1; i {minIndex : ifor j : i 1; j < n; j {if arr[j] < a…

一篇关于预测“未来”的教程:运行在 Intel AIxBoard™ 开发板上的 TDengine

英特尔数字化开发套件 AIxBoard 是一款 AI 架构的人工智能嵌入式开发板&#xff0c;体积小巧功能强大&#xff0c;可以在时序数据预测、图像分类、目标检测分割和语音处理等应用中并行运行多个神经网络。作为一款面向专业创客、开发者的功能强大的小型计算机&#xff0c;借助开…

牛客网Verilog刷题——VL48

牛客网Verilog刷题——VL48 题目答案 题目 在data_en为高期间&#xff0c;data_in将保持不变&#xff0c;data_en为高至少保持3个B时钟周期。表明&#xff0c;当data_en为高时&#xff0c;可将数据进行同步。本题中data_in端数据变化频率很低&#xff0c;相邻两个数据间的变化&…

字符串性能优化

String 对象作为 Java 语言中重要的数据类型&#xff0c;是内存中占据空间最大的一个对象。高效地 使用字符串&#xff0c;可以提升系统的整体性能。 来一到题来引出这个话题 通过三种不同的方式创建了三个对象&#xff0c;再依次两两匹配&#xff0c;每组被匹配的两个对象是否…

Eclipse使用Ctrl键导致程序卡死的解决方案

在Eclipse中&#xff0c;经常可以使用Ctrl鼠标单击&#xff0c;可以直接将编辑界面引导到相关的方法&#xff0c;属性&#xff0c;或者类。 这个功能确实非常好用&#xff0c;但是由于复制粘贴的功能快捷键也是Ctrl&#xff0c;以致我在快速进行操作的时候&#xff0c;Eclipse…

tinkerCAD案例:27. Build a Mobile Amplifier 构建移动放大器(2)

tinkerCAD案例&#xff1a;27. Build a Mobile Amplifier 构建移动放大器(2) 原文 step 1 Lesson Overview: 课程概述&#xff1a; Now we’re going to adapt the shape to your device! 现在&#xff0c;我们将根据您的设备调整形状&#xff01; step 2 o create an in…

【雕爷学编程】MicroPython动手做(25)——语音合成与语音识别2

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

浏览器安装selenium IDE插件并进行网页测试记录

Chrome开发者工具插件,谷歌浏览器开发者工具插件推荐下载_安装_教程-扩展迷 去官网直接搜索下载需要的插件就可。 插件下载安装-Chrome-扩展迷 下载好后解压&#xff1a; 打开Chrome谷歌浏览器&#xff1a; 设置>拓展程序>打开"开发者模式”>将下载好的seleni…

【多模态】21、BARON | 通过引入大量 regions 来提升模型开放词汇目标检测能力(CVPR2021)

文章目录 一、背景二、方法2.1 主要过程2.2 Forming Bag of Regions2.3 Representing Bag of Regions2.4 Aligning bag of regions 三、效果 论文&#xff1a;Aligning Bag of Regions for Open-Vocabulary Object Detection 代码&#xff1a;https://github.com/wusize/ovdet…

SciencePub学术 | 人工智能类重点SCIEEI征稿中

SciencePub学术 刊源推荐: 人工智能类重点SCIE&EI征稿中&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; 人工智能类重点SCIE&EI 【期刊简介】IF&#xff1a;6.5-7.0&#xff0c;JCR1区&#xff0c;中科院2区&#xff1b; 【出版社…

画架构图工具-haydn

Haydn解决方案数字化平台_海顿解决方案工具链-华为云 下图为haydn架构图示例 Haydn解决方案数字化平台_海顿解决方案工具链-华为云 1、vpc是一个很重要的元素&#xff0c;有网络隔离的作用。 2、OBS、CES、CTS&#xff0c;不需要画到vpc里面。 3、不在区域内的资源&#xf…

Panda 编译时原子化 CSS-in-JS 框架的跨平台方案

Panda 编译时原子化 CSS-in-JS 框架的跨平台方案 Panda 编译时原子化 CSS-in-JS 框架的跨平台方案 对编译时原子化CSS框架的思考编译时 CSS-in-JS 方案对比 LinariaPandacss总结 weapp-pandacss 介绍快速开始 pandacss 安装和配置 0. 安装和初始化 pandacss1. 配置 postcss2. …

Hbase pe 压测 OOM问题解决

说明&#xff1a;本人使用CDH虚拟机搭建了Hbase集群&#xff0c;但是在压测的时发现线程多个的时候直接回OOM,记录一下 执行命令 hbase pe --nomapred --oneContrue --tablerw_test_1 --rows1000 --valueSize100 --compressSNAPPY --presplit10 --autoFlushtrue randomWrite …

SDXL 1.0 介绍和优缺点总结

2023年7月26日:Stability. AI 发布SDXL 1.0&#xff0c;这是对其生成模型的又一次重大更新&#xff0c;带来了突破性的变化。 SDXL 1.0包括两种不同的模型: sdxml -base-1.0:生成1024 x 1024图像的基本文本到图像模型。基本模型使用OpenCLIP-ViT/G和CLIP-ViT/L进行文本编码。…