Qt之点滴
记录Qt中的一些小方法。
1 Qt之工程配置文件(.pro)
1.1 之编译前复制需要的文件
1 | # 获取绝对路径 |
2 qt之编译
2.1 更新qt自带的freetype
Qt的源码中自带的import_from_tarball.sh
脚本,可用于更新Qt内含的freetype库。Qt使用的Freetype库,位于${QT_SRC}/qtbase/src/3rdparty/freetype
,在该目录下有一个导入第三方库脚本:import_from_tarball.sh
,可以使用msys2将新版本的freetype导入到Qt中。
msys2命令格式,设 Qt src 位于 E:\qt\qt_build\qt-everywhere-src-5.15.1
1
2
3# sh import_from_tarball.sh freetype_src_dir freetype_to_dir
sh import_from_tarball.sh freetype-2.10.2 /E/qt/qt_build/qt-everywhere-src-5.15.1/qtbase/src/3rdparty/freetype${QT_INSTALL_DIR}/lib/libqtfreetype.a
使用时,需复制头文件(${QT_SRC}/qtbase/src/3rdparty/freetype/include
)至指定的位置,此处为 Qt 安装路径下的3rdParty
目录,以下内容抄自%{cmake}/share/cmake-3.18/Modules/FindFreetype.cmake
文件
1 | set(QT_DIR D:/Dev/Qt/5.15.1/qt) |
2.2 qmake
用法
1 | # Qt 内置常量,可通过 qmake -query 查询 |
3 Qt容器类使用基于范围的for
循环(Range-Based for Loops)
C++11
规范引入了基于范围的for
循环,例如对std:vector
:
1 | for (auto name: names) { |
C++11
编译器将上述内容翻译为:
1 | for (auto name: names) { |
编译器使用begin
/end
迭代器遍历整个names
集合。
由于Qt采用copy-on-write
的隐式共享特性,在Qt容器类中使用基于范围的for
循环时,由于for
循环内部调用begin()
和end()
函数,会产生一个非const
的容器对象从隐式共享的数据中分离出来(cause a non-const container to detach from shared data),从而出现容器类深拷贝
的情况:
1 | QDir dir; |
解决方案很简单:
- 如果容器类是一个
non-const
的右值(rvalue
), 定义一个const
变量保存之,然后再遍历:
1 | const auto strings = functionReturningQStringList(); |
- 如果容器类是一个
non-const
的左值(lvalue
), 先使该容器对象成为const
,然后再遍历,如果无法解决,则使用std::as_const()
(C++17
后支持)或qAsConst()
:
1 | for (const QString &s : qAsConst(container)) |
上述处理后,没有分离,也没有非必须的"深拷贝",使性能和可读性最大化。
4 Qt之结构化绑定(Structured bindings)
Python
中常用类似解绑做法:
1 | # python |
从C++17
开始,C++支持类似操作,称为结构化绑定:
1 | QPair<QString, QString> values = {"new", "old"}; |
由于与std::map
的API不兼容,QMap
/QHash
无法使用结构化绑定实现参数解绑,问题在于Qt的迭代器仅返回值而非std
中的键值对:
1 | QMap<QString, QString> map = {{"hello", "no1"}, {"hi", "no2"}}; |
Qt
自 5.10
起提供了 key_value_iterator
和 const_key_value_iterator
迭代器:
The QMap::key_value_iterator typedef provides an STL-style iterator for QMap and QMultiMap.
QMap::key_value_iterator is essentially the same as QMap::iterator with the difference that operator*() returns a key/value pair instead of a value.
1 | QMap<QString, QString> map = {{"hello", "no1"}, {"hi", "no2"}}; |
因此使用一个封装类做一个简单的转换,将keyValueBegin
/keyValueEnd
转换为begin
/end
,就可以使用基于范围的for
循环结合结构化绑定,直接实现变量解绑:
1 |
|
注意,无论是否使用const
关键字,key和value均为非const
引用,由于T &m_data
非const
,对上述值的修改,会影响原来的容器
1 | for (const auto [key, value]: asKeyValueRange(map)) { |
修改也很简单,将 T &m_data
定义改为 const T &m_data
即可保证是常引用。 1
2
3
4
5// 使用 const T &m_data 时:
for (auto [k, v] : asKeyValueRange(map)) {
v = k + "[" + v + "]"; // 报错
qDebug() << k << v;
}
Qt 6.5.2 中,
asKeyValueRange
已经成为QMap
的成员函数,可以直接使用,该函数返回值仍然为非常引用,对内容的修改直接会修改原QMap容器内的值;
5 Python-like enumerate
对容器类使用enumerate
同时获得索引和容器值:
1 | /** ********************************************************************************** |
参考
Qt, range-based for loops and structured bindings
(https://www.kdab.com/qt-range-based-for-loops-and-structured-bindings/)Goodbye, Q_FOREACH
(https://www.kdab.com/goodbye-q_foreach/)