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

网站后台源码wordpress手机怎么使用

网站后台源码,wordpress手机怎么使用,网站访问量大打不开,网站设计原则有哪些这段内容讲的是 Qt 容器#xff08;Qt Containers#xff09;和标准库容器#xff08;STL Containers#xff09;之间的选择和背景#xff1a; 主要观点#xff1a; Qt 容器的历史背景 Qt 自身带有一套容器类#xff08;如 QList, QVector, QMap 等#xff09;#…这段内容讲的是 Qt 容器Qt Containers和标准库容器STL Containers之间的选择和背景 主要观点 Qt 容器的历史背景 Qt 自身带有一套容器类如 QList, QVector, QMap 等主要是因为历史原因 早期Qt需要支持没有标准库STL支持的平台。避免将标准库的符号暴露在Qt的ABI应用二进制接口中保证二进制兼容性。 现在的情况Qt 5及以后 Qt 5以后已经假定目标平台有“可用的、可用的STL实现”。这意味着Qt本身内部开始依赖标准库标准库的可用性已成为前提。 Qt容器在API中的角色 Qt依然在API中使用Qt容器且这些容器对应用程序开发者开放。但对于新项目和业务代码建议优先使用标准库容器只有在必要时才用Qt容器。 总结 Qt容器是为了历史兼容和API稳定性存在。对于大多数现代C项目尤其是跨平台和与第三方库交互推荐使用标准库容器如 std::vector, std::map 等。仅在和Qt框架接口交互时或者特殊性能需求时考虑Qt容器。 这部分内容详细对比了 Qt 容器和 C 标准库容器的设计哲学及对应关系并介绍了一个特殊的 Qt 容器——QVarLengthArray。以下是总结 Qt 容器设计哲学 vs 标准库设计哲学 特点Qt容器标准库容器目标足够好主要用于构建 GUI真正通用适合各种场景API 风格使用 camelCase例如 isEmpty()使用 snake_case例如 empty()设计重点易用性和API易发现性高效和正确性典型用法示例QVectorint v; v 1 2 3;std::vectorint v; v.push_back(1);拷贝行为拷贝可能较“便宜”浅拷贝或引用计数机制拷贝通常是深拷贝算法实现作为成员函数如 contains()通过标准算法如 std::find Qt与标准库对应容器对照表 Qt 容器标准库容器QVectorstd::vectorQList—QLinkedListstd::listQVarLengthArray—QMapstd::mapQMultiMapstd::multimapQHashstd::unordered_mapQMultiHashstd::unordered_multimapQSetstd::unordered_set QVarLengthArray 介绍 QVarLengthArray 是 Qt 中一个特殊的容器类似于 std::vector但它预先在栈上分配一定的空间避免频繁的堆分配。类似于 Boost 的 small_vector支持所谓的“短小优化”SSOsmall string optimization思想提高小规模数据的性能。适用于大多数情况下容器元素数量不会超过某个固定值的场景。示例声明QVarLengthArrayObj, 32 vector; 表示预分配32个对象的空间。 这段内容进一步强调了 QList 以及整体 Qt容器 的局限性并建议更倾向使用标准库容器。总结如下 QList 的特性和问题 不是链表而是基于数组实现的列表。对于存储大于指针大小的对象非常低效因为它会为每个对象单独分配堆内存。建议避免使用 QList除非别无选择。自己写代码时优先使用 QVector。 不建议使用 Qt 容器的理由 Qt 容器维护和更新不活跃缺乏新特性。STL 容器更快生成的代码更小且经过更多测试。Qt 容器功能远不及 STL例如 存放的类型必须可默认构造和可复制。没有异常安全保证。缺少许多 C98/C11/C17 新增的API如范围构造、插入、就地构造emplace、基于节点的操作等。不支持灵活的分配器、比较器、哈希函数等自定义操作。 Qt 容器的API不一致比如 QVectorT 支持 append(T)但 QListT 不支持。还有在 resize、capacity、shrink 等行为上的差异。Qt 容器API 与 STL 容器存在微妙差异可能带来使用上的困扰。 建议 优先使用 STL 容器如 std::vectorstd::mapstd::unordered_map 等。只有在必须与 Qt API 交互时才考虑使用 Qt 容器。 这段主要给出了在实际项目中选择容器的建议核心点总结如下 选择哪个容器 STL 容器大多数情况下性能和特性都优于 Qt 容器。Qt 自身实现内部也开始采用 STL 容器说明它们的优势。Qt 的 API 仍然暴露 Qt 容器无法轻易替换因为 Qt 有强 API 和 ABI 兼容性承诺。 应用程序的推荐策略 优先使用 STL 容器。仅在以下情况考虑使用 Qt 容器 没有对应的 STL 或 Boost 容器这几乎不存在。与 Qt 或基于 Qt 的库接口交互时。 如果用到了 Qt 容器尽量避免来回转换 STL 和 Qt 容器。 保持使用 Qt 容器减少性能开销和复杂度。 简单来说除非为了兼容 Qt 接口推荐用 STL 容器既现代又高效。 关于 resize、capacity、shrink 这几个行为Qt 容器和 STL 容器确实存在一些细节差异 1. resize() STL 容器 resize(n) 会调整容器大小到 n如果变大会用默认值或指定值填充新增元素。对于 std::vector新增元素构造且初始化。可以保证元素连续且大小准确。 Qt 容器比如 QVector resize(n) 也会调整大小但内部实现可能采用引用计数共享数据。新增元素初始化行为和 STL 类似但某些情况下效率可能略差。QList 的行为因内部结构不同resize() 可能导致额外的内存分配效率不佳。 2. capacity() STL 容器 capacity() 返回当前已分配但未使用的内存空间大小。std::vector 会预先分配一定空间减少扩容次数。可以通过 reserve() 来预先分配容量避免多次重新分配。 Qt 容器 capacity() 也返回预分配空间大小。但 Qt 容器尤其是老版本对容量管理不如 STL 灵活扩容策略可能不同。不能像 STL 一样明确调用 reserve() 保证容量。 3. shrink_to_fit() STL 容器 C11 引入的函数shrink_to_fit() 用于请求减少容量以匹配当前大小。实现是非强制的但多数现代库会释放多余内存。提高内存利用率避免浪费。 Qt 容器 大多数 Qt 容器没有 shrink_to_fit() 接口。只能通过拷贝或交换技巧手动释放多余容量比如重新构造一个容器拷贝数据。缺乏直接控制容量的函数不够灵活。 额外说明 Qt 容器内部通常使用引用计数和共享数据的技术这导致某些操作比如 resize会更复杂可能出现延迟复制copy-on-write。STL 容器行为更加透明直接便于性能优化和行为预测。 总结 操作STL 容器Qt 容器resize()直接调整大小初始化新增元素类似但可能因共享数据延迟复制capacity()返回预分配容量可用 reserve 控制返回预分配容量容量管理不够灵活shrink_to_fit()标准接口尝试释放多余内存无对应接口需手动技巧释放多余容量 总结来说针对 Qt 6 Qt 容器必须继续保留确保兼容性和稳定的 API/ABI不会做大破坏性改动。QList 在 Qt 中使用非常广泛但它其实并不是一个理想的线性容器。未来有可能让 QList 直接成为 QVector 的别名typedef简化内部实现。同时Qt 可能会推出一个新的容器类型比如 QArrayList来替代 QList 的部分功能提供更好的性能和设计。Qt 容器通过类型特征type traits来优化性能尤其是判断一个类型是否支持relocatable可重定位。如果类型是可重定位的容器扩容时可以直接用 realloc 这样高效的内存操作而不需要一个个移动元素性能大幅提升。使用 Qt 容器时建议用 Q_DECLARE_TYPEINFO 宏来告诉 Qt该类型是否可重定位从而启用优化。一些典型例子 简单结构体如 IntVector通常是可重定位的。有指针指向自己或有内部联系的结构如 TreeNode通常不可重定位因为移动内存会破坏指针。有短字符串优化SSO的字符串类型如果内部指针指向内部缓冲区也不可重定位。 这个机制可以显著提高 Qt 容器的性能前提是正确声明类型信息。 编译器不能自动判断类型是否可重定位relocatable需要开发者手动标注。 Qt 通过宏 Q_DECLARE_TYPEINFO(Type, Kind) 来告诉容器该类型的“性质”Kind 可以是 Q_PRIMITIVE_TYPE 类型非常简单比如 int任何位模式都是有效的构造和析构可以跳过直接内存拷贝即可 Q_MOVABLE_TYPE 类型可被内存移动如用 memmove 或 realloc但仍然调用构造和析构函数 Q_COMPLEX_TYPE默认 普通复杂类型需要正常调用构造、复制、析构 EASTL 有类似机制EASTL_DECLARE_TRIVIAL_RELOCATE而 STL 标准库本身没有明确这个特性。 这让 Qt 容器能根据类型特性选择最优内存操作提高性能。 每次定义可能会被放入 Qt 容器的自定义类型时都应该用 Q_DECLARE_TYPEINFO 显式声明其类型信息。 例如struct IntVector {int size, capacity;int *data; }; Q_DECLARE_TYPEINFO(IntVector, Q_MOVABLE_TYPE);如果之后再加这个 trait有可能会导致 ABI 兼容性问题影响程序稳定性和升级安全。 所以建议一开始就定义好避免后期修改带来的麻烦。 Qt 的**隐式共享Implicit Sharing**核心思想是 对象内部包含一个指向实际数据pimpl的指针这个数据块有一个引用计数器refcount。创建对象时refcount 1。拷贝对象时只拷贝指针refcount 1。调用 const 方法不改数据refcount 不变。调用非 const 方法时如果 refcount 1说明数据被共享必须先detach深拷贝数据保证修改不会影响其他对象写时拷贝Copy-On-Write。 这样设计的好处是拷贝操作很轻量只增引用计数节省性能。保证数据修改时不会影响到其他对象实现值语义。但需要注意调用非 const 方法会触发隐式深拷贝可能会有性能开销。 这个机制常见于 Qt 的字符串QString、容器等类。 要小心“隐藏的 detach”即你可能没有意识到调用了非 const 方法导致了拷贝开销。 这里演示了隐式共享在 QVector 中的实际效果。 示意过程是这样的 QVectorint v {10, 20, 30}; QVectorint v2 v; // 复制v不会马上拷贝数据而是共享内部数据v 和 v2 共享同一块内存payload里面存着 {10, 20, 30}。引用计数refcount为 2表示两个 QVector 对象共享同一数据。这时内存只保存了一份数据拷贝成本很低。 只有当你对 v 或 v2 调用非 const 方法修改操作时如果 refcount 1就会触发 detach深拷贝数据分配独立内存避免数据冲突。 总结复制 QVector 很轻量共享数据 refcount修改共享数据前会触发深拷贝detach 这个例子具体展示了隐式共享copy-on-write机制在 QVector 修改时的行为 QVectorint v {10, 20, 30}; QVectorint v2 v; // v2 共享 v 的数据refcount 2payload {10, 20, 30} v2[0] 99; // 修改 v2第一个元素变成 99过程分析 初始时v 和 v2 共享同一份数据payload内容是 {10, 20, 30}引用计数是 2。当执行 v2[0] 99 这个写操作时v2 检测到引用计数大于 1表示数据被共享触发detach。detach 意味着 v2 会进行一次深拷贝分配自己的内存来存储数据。修改只会影响 v2v 依旧保持原数据 {10, 20, 30}。结果是 v 仍然是 {10, 20, 30}v2 变成了 {99, 20, 30}两者的数据不再共享引用计数分别为 1。 这个机制保证了 复制对象时开销很小都是共享数据。只有写操作时才真正做深拷贝保证数据安全。 这是 Qt 容器里隐式共享的核心思想也是性能优化的关键点。 Qt 的 Implicit Sharing隐式共享 的总结。以下是对这段内容的详细理解解释 什么是 Implicit Sharing 隐式共享是一种 “写时拷贝”Copy-On-Write, COW 机制结合了引用计数和延迟深拷贝的技术核心目的是 节省内存提高性能简化代码书写 为什么有用 “This mechanism makes writing code a lot simpler” 这句话的意思是你可以像写普通值一样写 Qt 的类例如 QString, QByteArray, QVector 等而不用担心性能问题。 比如 QString a hello; QString b a; // 不会拷贝数据只是增加引用计数 b[0] H; // 触发深拷贝detacha 仍然是 hellob 变成 Hello你可以放心用 值返回return QString拷贝赋值QString b a不需要频繁考虑性能陷阱 哪些 Qt 类支持 Implicit Sharing “The great majority of Qt value classes are implicitly shared” 常见支持隐式共享的类包括 类型是否支持隐式共享QString是QByteArray是QVariant是QImage / QPixmap是QVector是QList是QMap / QHash是QVarLengthArray否 例外QVarLengthArray 是值语义容器不走引用计数它在栈上直接分配数据所以不隐式共享。 工作原理简述 拷贝对象时不复制数据只是引用计数 1。写入对象时如果引用计数 1就自动复制一份detach以避免影响其他对象。析构对象时引用计数 -1当为 0 时释放资源。 总结 隐式共享是 Qt 的一大特色。它允许你 写出高效、简洁的值语义代码不担心性能陷阱但也要注意写操作即使看起来是 const可能隐式触发深拷贝。 “Implicit sharing and containers: where’s the catch?”指出了 Qt 隐式共享容器的一些隐藏陷阱和误区。以下是逐句解析与理解 隐式共享和容器问题出在哪 • Handing out references to data inside a container does not make the container unshareable 意思 即使你将容器中的元素通过引用的方式返回或传递出去比如 QVectorint v1 {1, 2, 3}; QVectorint v2 v1; // 现在引用计数是 2 int ref v1[0]; // 获取引用此时 v1 仍然是可共享的Qt 不会自动触发 detach。 也就是说只是访问引用不会破坏共享关系。 • Its easy to accidentally detach a container 意思 一旦你对容器做了写操作哪怕是间接的就会触发 detach深拷贝。比如 v2[0] 100; // 一写就会 detach变成独立的副本这种操作很容易发生在你没意识到的地方从而悄悄改变了对象的共享状态。 • Accidental detaching can hide bugs 意思 这种悄悄发生的 detach 行为可能导致 bug 被隐藏因为 你以为两个对象共享同一份数据如 v1, v2但其实不再共享导致数据不同步、调试困难在多线程或资源受限环境中尤其危险。 例如 if (v1 v2) {doSomething(); // 你以为它们是同一份数据但可能早就 detach 了 }• IOW, its not just about performance IOW In Other Words换句话说 不是只有性能问题还是“正确性问题” 深拷贝带来的性能开销固然重要代码逻辑混乱、共享状态错乱、数据不一致更加危险这些 bug 可能非常隐蔽特别是当代码中混入了隐藏的 detach 操作。 • Code polluted by (out-of-line) detach/destructor calls 意思 编译后的代码里会因为 Qt 的隐式共享机制出现许多 隐藏的拷贝构造函数调用深拷贝detach操作析构函数调用 这会让代码生成“变重”、函数调用栈变复杂甚至会破坏 inlining从而降低性能或调试可读性。 总结使用 Qt 隐式共享容器的注意事项 项目建议写入操作明确知道何时触发了 detach多对象共享容器时小心副作用、不可预期的独立副本性能敏感代码尽量使用 std::vector 等无隐式共享的 STL 容器传引用/指针访问内部数据知道不会破坏共享状态但不要写入 这段内容解释了 Qt 隐式共享容器如 QVector中一个容易被忽略的陷阱引用包括迭代器不会阻止容器被拷贝detach可能导致代码行为与你预期的不同。 下面是逐句解释和深入理解 Returning references to data inside a container从容器中返回引用 “Handing out references to data inside a container does not make the container unshareable” 意思是 即使你取出了容器中某个元素的引用这个容器仍然是“共享的”Qt 不会因为你持有引用而主动拷贝detach数据。 也就是说这不是 COWCopy-On-Write触发的条件。 “E.g. of such references: iterators” 像下面这样 QVectorint v {10, 20, 30}; auto it v.begin(); // 拿到迭代器 int ref v[0]; // 或直接取引用这些引用/迭代器不会改变引用计数也不会让 Qt 自动 detach。 例子分析 QVectorint v {10, 20, 30}; auto r v[0]; // 取引用 QVectorint v2 v; // 现在 v 和 v2 是共享的refcount 2 r 99; // 修改通过 v 的引用会影响共享数据图示如下内存共享前 v 和 v2 共用同一个 payload: payload [10, 20, 30] refcount 2当执行 r 99; 时没有触发 detach因为 r 是直接引用底层数据。 结果是 v [99, 20, 30] v2 [99, 20, 30] -- 也被修改了你以为 v2[0] 还是 10结果 断言失败 assert(v2[0] 10); // fails!关键陷阱总结 行为是否触发 detach说明赋值一个容器不触发引用计数增加调用非 const 成员函数可能触发如 v[0] 99会自动复制detach手动获取元素引用然后修改不自动 detach数据是共享的两个容器都会变使用迭代器修改内容不自动 detach同样直接影响共享内存 正确做法建议 避免持久使用引用或迭代器后再修改容器副本如果你想“安全修改一个副本”请手动 detachQVectorint v2 v; v2.detach(); // 强制深拷贝 v2[0] 99; // 不会影响原 v使用 STL 容器如 std::vector时这种问题不会出现因为没有隐式共享。 Qt 隐式共享容器中「意外的深拷贝accidental detach」问题。这是 Qt 使用者常常忽略的一大坑。以下是详细解释 你需要理解的核心要点 例子 QVectorint calculateSomething(); const int firstResult calculateSomething().first(); 问题分析 QVectorT::first(); // 是非 const 的返回 T这就意味着 calculateSomething() 生成了一个临时的 QVectorint。.first() 是 非 const 成员函数所以 Qt 会 触发 detach即 deep copy 临时对象的数据。 但其实你并不需要修改这个容器你只是想拿第一个值但 Qt 没法知道你的意图调用了非 const 版本就要执行 Copy-On-Write。 结果 你只是想 const int x QVectorint{10, 20, 30}.first(); 但 Qt 背后悄悄 创建 QVector 临时对象进行一次深拷贝detach然后返回引用其实没用到 这就引发了不必要的内存分配和复制——而你完全没有意识到 正确的写法 const int firstResult calculateSomething().constFirst();这样 .constFirst() 是 const 成员函数不会触发 detach没有不必要的深拷贝返回值仍然是你需要的第一个元素但是只读的 总结Qt 中意外 detach 的教训 错误写法原因正确写法v.first()非 const 成员函数可能 deep copyv.constFirst()v.last()同上v.constLast()v[i]返回引用非 const可能 detachv.at(i) 或 const auto val v[i]; 怎么发现这些问题 它们在编译时不会报错但会在 heap profiler如 massif, heaptrack中看到内存突增一旦你分析出代码里这些细节会发现许多“不该拷贝的地方在偷偷拷贝”。 Qt 容器的隐式共享implicit sharing机制以及 accidental detach意外拷贝 带来的陷阱。现在我们来逐条解析你说的这个更严重的问题 1. Accidental Detach 导致的 Bug不是性能问题而是逻辑错误 场景代码 QMapint, int map; // ... if (map.find(key) map.cend()) {std::cout not found std::endl; } else {std::cout found std::endl; }问题来了 map.find(key) 是一个 非常量成员函数non-const它可能导致容器 detach即做一次深拷贝复制 pimpl。而你已经提前调用了 map.cend()它指向 原始容器的末尾之后容器被 detach 成新副本map.find() 返回的是 新副本的迭代器两个 end 迭代器 来自不同的容器副本它们 不相等 结果 哪怕 key 根本就不在原始 map 中也可能打印 found这就不是性能问题了而是一个 逻辑 Bug非常隐蔽、危险 正确做法使用 const 方法 if (map.constFind(key) map.cend()) {std::cout not found std::endl; }或者等价更安全 if (!map.contains(key)) {std::cout not found std::endl; }总结建议Qt 容器 隐式共享 非 const 方法 潜在 Bug 场景错误方法原因正确做法查找元素map.find(key)可能触发 detach破坏逻辑判断map.constFind(key)访问第一个元素v.first()非 const 方法可能导致 deep copyv.constFirst()遍历时比较混用 iterator 和 const_iterator来自不同容器副本比较结果错误统一用 const_iterator 标准库的立场 vs Qt 的立场 C STLQt避免隐式共享机制copy-on-write依赖 implicit sharing强调明确语义、值语义更偏重方便与 API 一致性更安全、更一致更方便、更快捷但埋雷多如果你开发的是性能敏感或逻辑严谨的模块比如底层库、工具链建议 尽量使用 STL 容器只在与 Qt API 交互时使用 Qt 容器严格区分 const 和非 const 使用使用静态分析工具如 Clazy来检测 Qt-specific misuse。 千万不要再用 foreach 或 Q_FOREACH 原因总结 foreach (var, container)等价于以下代码 {const auto _copy container; // 拷贝了整个容器auto it _copy.begin(), end _copy.end();for (; it ! end; it) {var *it;body;} }严重问题 1. 容器整体拷贝一次 即使你只想遍历但 Qt 容器采用隐式共享机制copy-on-write会把整个容器 复制一份这完全是你意料之外的。 2. 逻辑错误隐患 容器变了你拿到的是副本里面元素可能不对还以为遍历的是原始容器。 3. 性能问题非常严重 在有大量数据或频繁迭代的场景下每次循环都在悄悄 deep copy。 正确做法使用 C11 的 range-based for for (const auto value : container) {// Safe, efficient, no copy }不会触发隐式深拷贝更现代更清晰完美支持 STL 和 Qt 容器如 QVector, QStringList 尤其小心 Qt 容器 由于 Qt 容器隐式共享implicit sharing Q_FOREACH 的复制行为一起使用等于 踩雷必炸。 例如 QStringList list {a, b, c}; foreach (QString s, list) {// 修改 s 没问题但 list 是被拷贝的副本 }你以为你在修改 list其实根本没改到 永久禁用建议企业/团队级别 在 .pro 或 CMakeLists.txt 中添加 DEFINES QT_NO_FOREACH这会在你代码中使用 foreach / Q_FOREACH 时 编译失败。 总结 特性Q_FOREACHC11 range-for是否复制容器会复制不会支持 STL 容器否是是否安全高效否是是否推荐使用完全不推荐强烈推荐 foreach / Q_FOREACH 使用建议总结 “优点”其实是个误导 “可以安全修改原容器” 其实是误导因为循环中你操作的是拷贝副本不是原容器。真正修改容器行为反而不可控、不清晰。 缺点致命 缺点说明总是复制容器Q_FOREACH 会拷贝一份容器无论你愿不愿意即使你只读。对 STL 容器非常昂贵拷贝的是 const 容器无法修改容器元素因为元素是 const可读性差隐式语义不清楚容易出 bug与现代 C 不兼容不支持 STL 容器不支持迭代器不支持 structured bindings 等将在 Qt 6 中被移除官方明确计划废弃此功能 最佳实践 使用 C11 的 range-based for for (const auto item : container) {// 安全、高效、清晰 }禁用 Q_FOREACH / foreach 在项目中定义 DEFINES QT_NO_FOREACH // for qmake # 或 add_definitions(-DQT_NO_FOREACH) // for CMake这将使编译器在你使用 foreach 时直接报错强制你使用现代 C。 额外建议 替换建议替代语法foreach (auto x, list)for (const auto x : list)foreach (QString s, strings)for (const QString s : strings)foreach (int i, QVectorint)for (int i : QVectorint)如果你愿意我可以 扫描一个项目中的所有 foreach 并一键转换为 C11 语法或者写一个 clang-tidy 规则 / Python 脚本帮助自动替换。 这一节讲的是 range-based for 循环基于范围的 for 循环 在 Qt 和 STL 容器上的行为细节尤其是它可能引发的 隐式分离implicit detach 问题。 Range-based for 的真实展开形式 for (var : container) body;等价于 {auto c (container);auto i begin(c);auto e end(c);for (; i ! e; i) {var *i;body;} }STL 容器如 std::vectorT的行为 i 和 e 是 std::vectorT::iterator如果你不在 body 中修改元素或容器没有副作用。这也是现代 C 推荐的方式。 Qt 容器如 QVectorT的行为 i 和 e 是 QVectorT::iterator不是 const_iterator。即使你不在 body 中修改容器也可能导致 隐式 detach 因为 Qt 容器在调用 non-const 成员函数如 begin() 和 end()时如果 refcount 1会触发深拷贝detach。 例如 QVectorQString v ...; for (const auto s : v) {qDebug() s; }乍一看没问题但 v.begin() 和 v.end() 是 非 const 成员函数可能导致 性能开销触发 deep copy行为变化影响共享数据的其它副本 正确做法避免 detach 推荐方式明确使用 const 或 const_iterator // 使用 const 引用避免 detach for (const QString s : v) {qDebug() s; }或者如果你写模板代码优先使用 const QVectorT 参数这样 begin() 会是 const_iterator避免 detach。 小结如何安全使用 range-based for 情况建议使用 STL 容器直接使用 range-based for安全高效使用 Qt 容器容器变量加 const元素加 const不确定是否 detach用 const_iterator 避免陷阱 这一部分是对 Qt 的 Q_FOREACH 和 C11 的 range-based for loop 在使用 Qt 容器和 STL 容器时的行为差异总结。下面帮你归纳一下关键点 range-based for vs Q_FOREACH 对比总结 容器类型Q_FOREACHrange-based for (auto : c)range-based for (const auto : c)Qt 非 constOKcheap可能会 detach非 const 迭代器可能会 detachbegin() 不是 constQt constOKcheap不会 detachconst 迭代器不会 detachconst 迭代器STL 非 const会 deep copy复制一份OKOKSTL const会 deep copyOKOK 为什么有这些区别 Q_FOREACH 的问题 始终复制容器哪怕是 const 容器对于 STL 容器来说是灾难性的深拷贝。对 Qt 容器没什么问题因为 Qt 使用了 implicit sharing所以复制是廉价的。缺点是代码难以推理性能不透明因此 Qt 6 已废弃 Q_FOREACH。 range-based for 的细节 for (auto item : container) {// ... }如果 container 是 Qt 非 const 容器 调用的是 begin() 和 end()它们是 非常量成员函数。如果容器被共享refcount 1会发生 detach深拷贝。 如果 container 是 const就会使用 const_iterator不会触发 detach。 实践建议写 Qt 代码时 避免使用 Q_FOREACH在项目中定义#define QT_NO_FOREACH优先使用 range-based for但要注意const QVectorint vec ...; for (const auto x : vec) { // 安全不会 detach... } QVectorint vec ...; for (auto x : vec) { // 可能 detach如果被共享... }如果一定需要修改容器或元素考虑 保证容器未共享或使用 detach() 手动控制。 非 const Qt 容器使用 range-based for 循环时要小心可能触发隐式 detach。如果不修改容器尽量用 const 容器或者通过 qAsConst()Qt5.7起或 std::as_const()C17起将容器转换为 const。不能对临时rvalue直接使用 qAsConst()这种情况下先用 const 引用绑定再循环。 这样可以避免不必要的深拷贝提升性能且代码更安全。 Clazy它是基于 Clang 的开源静态分析工具专门针对 Qt 代码。总结一下 Clazy 类似于 clang-tidy但聚焦于 Qt 风格和 Qt 特有的坑。它自带 50 规则检查比如 detaching-temporary检测隐式 detach 相关问题strict-iterators检测迭代器使用missing-typeinfo缺少类型信息foreach检测不建议用的 Qt foreach 它还能自动提供 fix-it帮你自动改代码。即使是 Qt 自己的代码库也有不少问题被 Clazy 检测出来。建议定期用 Clazy 扫描你的代码修复警告提升代码质量和性能。 这工具对保持 Qt 代码库的健康和现代化很重要尤其是避免隐式 detach 等细节导致的性能问题。 Qt 字符串类创建方式 常见的创建字符串的方式很多 直接字符串字面量 stringQByteArray(string) —— 字节数组无编码信息QByteArrayLiteral(string) —— 编译时常量不分配内存QString(string) —— UTF-16编码字符串分配内存QLatin1String(string) —— 轻量视图适合拉丁1编码QStringLiteral(string) —— 编译时UTF-16常量不分配内存Qt 5.9QString::fromLatin1(string) —— 从Latin1编码构造QString::fromUtf8(string) —— 从UTF-8编码构造tr(string) —— 用于国际化的字符串QStringView(ustring) —— 轻量视图不分配内存 QByteArray 表示字节序列类似 std::string不包含编码信息隐式共享copy-on-write构造函数会分配内存QByteArray::fromRawData() 可以避免部分分配QByteArrayLiteral() 不分配内存存储于只读段适合静态数据 QString 使用 UTF-16 编码支持 Unicode 操作优于 std::u16string隐式共享构造函数会分配内存QString::fromRawData() 可以避免分配只读视图推荐使用 QStringView 作为轻量字符串视图QStringLiteral() 自 Qt 5.9 起不分配内存数据存储在只读段适合字符串常量 总结用 Qt 来管理 Unicode 字符串选 QString如果只读且想避免拷贝选 QStringView 或 QStringLiteral。用 QByteArray 处理原始字节流或二进制数据。 “Latin1” 是“ISO 8859-1”编码的简称全称是 ISO/IEC 8859-1: Latin Alphabet No. 1。 简单来说它是一种单字节字符编码使用 1 个字节8 位表示一个字符能表示西欧主要语言的字符集比如英语、法语、德语、西班牙语等范围覆盖了 0x00 到 0xFF 共 256 个字符其中前 128 个字符和 ASCII 码完全一样它不支持像中文、日文、韩文等复杂字符只适合基本拉丁字母和西欧符号在 Qt 里QLatin1String 是对 Latin1 编码字符串的轻量包装用来高效处理这类字符串避免转码成本。 总结Latin1 是一种旧式的、西欧字符编码适合只包含拉丁字母的文本不支持多语言 Unicode。 简单总结一下 QLatin1String 的作用和特点 它是一个轻量的字符串包装类只包含一个 const char* 指针和字符串长度不做内存管理主要用来表示 Latin1ISO 8859-1编码的字符串字面量比如代码里的 foo用于 QString 相关函数的重载避免不必要的临时 QString 分配和转换提高性能例如QString::startsWith() 同时有两个版本一个接受 QString一个接受 QLatin1String后者性能更好因为不产生临时字符串。 你可以把它看成是 Qt 里对纯 ASCII 或 Latin1 字符串字面量的一个“快捷通道”用来减少字符串转换和内存开销。 总结一下这段内容的重点 Qt 的主要字符串类是 QString 和 QByteArray。这几年对它们的改进不多保持了比较稳定的设计。从 Qt 5.9 开始QStringLiteral 和 QByteArrayLiteral 这两个宏的实现优化了——它们不会再动态分配内存而是直接使用编译时生成的静态内存这样可以显著提升性能。 简单总结一下 QStringView 从 Qt 5.10 引入的类型是一个 非拥有non-owning 的字符串视图类似于 C17 标准的 std::u16string_view。它直接指向一段 UTF-16 编码的字符序列比如 QString 内部存储格式但不负责管理这段内存。这样可以避免不必要的字符串拷贝提升性能特别适合只读访问场景。它提供了和 QString 大部分相似的只读接口方便使用。Qt 5.11 之后还会有更多的 API 和对 QStringBuilder 的支持使用体验会更好。 这段讲的是用 QStringView 作为函数参数类型的理由和好处重点如下 主要用途QStringView 适合作为函数参数尤其是函数需要读取字符串但不需要保留它时。如果函数需要一个 Unicode 字符串参数而且函数不会保存这个字符串推荐用 QStringView避免无谓的拷贝和分配。讨论了一个例子Document::find(StringType substring)StringType 用哪个类型好 QString 会强制调用者提供一个完整的 QString可能要动态分配内存或者使用 QStringLiteral 编译期字符串。QByteArray 不是 Unicode 安全的不适合处理 Unicode 文本。QLatin1String 虽然性能好因为是 Latin-1 编码但不 Unicode 安全不过可以做为额外重载实现快速路径。 总结QStringView 既支持 Unicode也避免了字符串不必要的复制非常适合作为 API 中接受字符串参数的接口类型。 这部分内容强调了 QStringView 作为接口类型的优势 QStringView 是 Unicode 安全的支持 UTF-16 编码。它不会进行内存分配alloc-free性能好。它可以从多种字符串源构造 编译时的字符串字面量ucompile time动态分配的 QString 对象甚至是一个大字符串的子串通过 QStringView(bigString, 40) 举例 class Document {iterator find(QStringView substring); }; Document d; d.find(ucompile time); // 传入编译时字符串字面量 QString allocatedString ...; d.find(allocatedString); // 传入动态分配的QString d.find(QStringView(bigString, 40)); // 传入大字符串的子串视图另外QStringView 还能作为“零分配”切割字符串的工具比如 QString str ...; QRegularExpression re(reg(.*)ex); QRegularExpressionMatch match re.match(str); if (match.hasMatch()) {QStringView cap match.capturedView(1); // 直接获取子串视图无需分配内存// ... }总结QStringView 让字符串处理既高效又安全非常适合作为函数参数和字符串子串的视图类型。 这一部分讲的是 QStringView 对 Qt API 的巨大影响 许多 Qt 函数现在接受 QString 参数但实际上并不需要持有字符串数据只是读取它们。在 Qt 6 中应该将这些函数改为接受 QStringView这样避免不必要的内存分配和拷贝提高性能。Qt 5 里还有一个类似的非拥有字符串视图类型叫 QStringRef但它设计有缺陷 它必须绑定到一个 QString 对象而不能直接表示任何 UTF-16 字符序列。因此灵活性较差。 QStringRef 只是权宜之计建议如果迫切需要用字符串视图可以暂时用它但随着 QStringView 在功能上达到 API 完整性QStringRef 会被废弃。 总结QStringView 是更现代、更高效的字符串视图接口未来 Qt 版本将以它为标准替代旧的字符串引用方式。 PODPlain Old Data 平凡数据类型 类型是否等同于“可搬移”relocatable 答案是否定的POD和可搬移是两个独立的概念。一个类型是否可搬移relocatable与它是否是POD类型无关。可搬移类型可能有非平凡的构造函数和析构函数比如 Qt 里基于pimpl指针实现的值类。反过来即使是平凡trivial的类型也未必可搬移——比如某些类型的对象地址本身代表身份identity搬移会破坏语义。所有C数据类型都是trivial但不一定是relocatable。 另外关于Qt中废弃的APIdeprecated APIsQt会标记旧的API为废弃虽然它们仍然可用且通过测试但Qt 6版本会移除大部分废弃API。Qt源码中通过宏 QT_DEPRECATED_SINCE(major, minor) 来标记并用 QT_DEPRECATED_X(建议替代方法) 生成编译警告。你可以通过定义宏来控制废弃API的使用 定义 QT_DEPRECATED_WARNINGS 来开启废弃API的警告。定义 QT_DISABLE_DEPRECATED_BEFORE版本号 来将早于某版本的废弃API使用视为错误。 例如 DEFINES QT_DISABLE_DEPRECATED_BEFORE0x050900这样就强制不允许使用 Qt 5.9.0 及更早版本中废弃的API。 总结 POD ≠ relocatable二者语义不同。Qt鼓励逐步迁移避免使用废弃API尤其是升级到Qt 6时。 QList 的核心问题是 它是一个“基于数组”的容器但内部实现是一个 void* 指针数组。根据存储的类型不同QList 可能存放指向元素的指针每个元素单独堆分配也可能直接存放元素本身。这种设计导致 每个元素单独堆分配时性能和内存效率都很差。元素存储方式依赖于平台32位 vs 64位和元素类型行为不稳定难以预测。对于小且可搬移的数据类型如 int 在64位平台非常浪费空间。 QList 优化了前置插入操作prepend但代价较大。尽管有这些问题QList仍然是Qt API中最常暴露的容器之一。 总结 不要在自己的代码中使用 QList推荐使用 QVector 或 STL 容器除非必须和 Qt API 交互。 总结下 QList 和 QVector 的区别和使用建议 推荐使用 QVector除非你必须调用需要 QList 的 Qt API。QVector 通常生成更少的代码性能也更好。QVector 在大多数操作上比 QList 快唯一例外是 经常在前面插入元素时QList表现可能更好。对非常大的对象进行重新分配时QList可能更合适。 QVector 重新分配时不保证引用或指针的有效性会失效。如果需要引用或指针保持有效建议用指针的容器比如 QVectorT*。 简单说就是 绝大多数情况下用 QVector只有少数场景考虑 QList。
http://www.pierceye.com/news/838801/

相关文章:

  • 贵阳建立网站聊城网站建设设计
  • 网站怎么设置关键词百度网址大全首页设为首页
  • 中企动力网站怎么样怎么做公司内网网站
  • 求职网站网页模板一个网站可以做多少个小程序
  • 深圳市住房和建设局网站登录怎样在百度建网站
  • 外国做视频在线观看网站asp简单网站开发
  • 介绍移动互联网的网站有哪些做网站时怎么选择数据库类型
  • 工厂的网站在哪里做的免费建站的软件
  • 中国电子系统建设三公司网站网站建设上如何提高市场竞争力
  • 青海住房和建设厅网站电子商务网站建设与管理教案
  • 免费在线自助建站搬瓦工可以长期做网站
  • 建设外贸网站报价外贸网站制作推广公司
  • 网站开发人员工作内容白沟做网站
  • 产品展示网站模板源码产品宣传
  • 国内wordpress有名的网站河南住房和城乡建设厅网站资质
  • 湛江seo建站wordpress5.1更新
  • 泊头公司做网站做网站价格差异很大
  • 网站开发啊wordpress 图片本地化
  • 尚品中国多年专注于高端网站建设免费加盟无需店面
  • 游标卡尺 东莞网站建设wordpress 域名解析
  • 站长工具视频怎么开免费网站
  • 网站地址怎么申请注册最近新闻大事
  • interidea 做网站网站域名备案 更改
  • 哈尔滨公司做网站动画设计的大学排名
  • 网站建设与网页制作试卷网站搜索引擎优化推广
  • 网站子目录设计网站开发数据库技术
  • 可以做音基题的音乐网站上海网站设计公司有哪些
  • 昆明做网站公司做家居的网站
  • 网站建设首选易网宣软文代发
  • 手机版网站开发用什么语言自己建设的网站怎么赚钱