目录
1.QStringLiteral
这是一个宏定义,定义如下:
QStringLiteral(str)
该宏为字符串字面量在编译时期产生一个QString对象。产生的字符串数据存放在编译对象文件的只读片区。如果你写代码像下面那样:
// hasAttribute函数接受一个类型为QString的参数
if (node.hasAttribute("http-contents-length")) //...
上面代码将会产生一个QString的临时对象,该临时对象作为参数传递给hasAttribute函数。这是非常昂贵的、效率非常低的操作。因为它涉及到内存分配、拷贝、转换字符串字面常量数据到QString内部编码等工作。通过QStringLiteral可以避免这种高代价操作。将上面代码改为如下:
if (node.hasAttribute(QStringLiteral(u"http-contents-length"))) //...
在这种情况下,QString内部数据将会在编译时期产生,而不是在每次运行时进行字符字面常量内存分配、字符字面量向QString对象的转换。
用QStringLiteral代替用双引号括起来的普通C++字符串字面常量,能够明显地提高从字符串字面常量创建QString对象实例的效率,且创建QString对象实例是在编译时进行的。
注意:当函数重载了QLatin1String参数时,调用重载QLatin1String类型的函数比QStringLiteral高效,因为它避免了转为QString,例如: 在Qt 5.14.1版本中,QString::operator==的重载函数共有如下三个:
bool operator==(QLatin1String other) const
bool operator==(const char *other) const
bool operator==(const QByteArray &other) const
其中第1个能直接比较QLatin1String,如下:
if (attribute.name() == QLatin1String("http-contents-length"))
的效率比如下代码效率高:
if (attribute.name() == QStringLiteral("http-contents-length"))
2.QLatin1String
QLatin1String类提供对基于US-ASCII/Latin-1 编码的字符串字面量的一层轻量封装。QString类有很多以const char*参数的重载成员函数,以接受字符串字面量,从而代替直接接受QString对象。这些重载函数包括:拷贝构造函数、比较操作符和其它函数,如 insert() 、replace()、 indexOf()。这些函数通常被优化,以避免从const char*构造出QString对象。类如,假想str是一个QString对象,如下代码:
if (str == "auto" || str == "extern"
|| str == "static" || str == "register") {
...
}
比下面的代码效率高:
if (str == QString("auto") || str == QString("extern")
|| str == QString("static") || str == QString("register")) {
...
}
因为前者不需要构造一个临时的QString对象,也不需对字符数据深度复制。
如果不想让应用程序访问、调用QString类中参数为const char*的系列函数,可以使用QT_NO_CAST_FROM_ASCII宏禁止C字符串字面量自动转为unicode编码的QString字符串。为了提供一种高效率地指定常量Latin-1编码的字符串方式,Qt提供QLatin1String类,该类是对const char*的一层轻量封装。上面的代码用QLatin1String改写为如下:
if (str == QLatin1String("auto")
|| str == QLatin1String("extern")
|| str == QLatin1String("static")
|| str == QLatin1String("register") {
...
}
从类型来说,这有点长,但是它提供了和前文第1个版本同样的优点,且当转为Latin-1 编码时,比QString::fromLatin1()执行快。
多亏了以QLatin1String参数的QString的构造函数!在需要用QString的地方都可以QLatin1String,例如:
QLabel *label = new QLabel(QLatin1String("MOD"), this);
注意:如果用QLatin1String作为实参传给被调用函数,而被调用函数实际没有重载QLatin1String参数,即该函数的形参不是QLatin1String类型,这将导致隐式类型转换到QString,从而触发内存分配,这通常是我们想避免的,在这种情况下,用QStringLiteral替代QLatin1String是不错的选项。 也就是说如下函数:
void fun(const QString& str)
{
// 其它代码
}
应优先采用
fun(QStringLiteral("csdn");
而不是采用
fun(QLatin1String("csdn");
而如果fun函数像如下那样:
void fun(const QLatin1String& str)
{
// 其它代码
}
应优先采用:
fun(QLatin1String("csdn");
3.性能验证
如下程序,验证了QLatin1Literal和QStringLiteral在效率上的区别:
#include <QtCore/QCoreApplication>
#include <QElapsedTimer>
#include<QDebug>
void testQStringLiteral(const QString&str)
{
//qDebug() << "QString" << str;
}
void testQStringLatin1(const QLatin1Literal& str)
{
// qDebug() << str;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
const auto nLoopTimes = 1E7;
QElapsedTimer t;
t.start();
for (auto i = 0; i < nLoopTimes; ++i)
{
testQStringLiteral("Literal");
}
qDebug() << "Literal:" << t.elapsed();
QElapsedTimer t1;
t1.start();
for (auto i = 0; i < nLoopTimes; ++i)
{
testQStringLiteral(QStringLiteral("Literal"));
}
qDebug() << "QStringLiteral:" << t1.elapsed();
QElapsedTimer t2;
t2.start();
for (auto i = 0; i < nLoopTimes; ++i)
{
testQStringLatin1(QLatin1Literal("Literal"));
}
qDebug() << "QLatin1Literal:" << t2.elapsed();
return a.exec();
}
上面代码用字符串字面量、 QStringLiteral宏、QLatin1String分别调用函数1E7次,在我本机耗费时间如下:
通过运行结果,可以看到:直接传递字符串字面量达到了惊人的7876ms;而采用QStringLiteral宏,只需760ms,而采用QLatin1String仅仅只需52ms。
4. 总结
- 当被调用函数参数是QString类型时,利用QStringLiteral宏传字符串比直接传字符串效率高。
- 当被调用函数存在有QString类型又存在QLatin1String类型的重载函数时,利用QLatin1String向函数传字符串比利用QStringLiteral宏向函数传字符串效率高。
- 当被调用函数参数仅仅存在QString类型,而不存在QLatin1String类型的重载函数时,利用QStringLiteral宏传字符串比利用QLatin1String向函数传字符串效率高。