我们先看一下Qt6官方的介绍,QStringLiteral(str)宏在编译时从字符串文本str中为QString生成数据。在这种情况下,从中创建QString是免费的,生成的字符串数据存储在编译的对象文件的只读段中。
如果您的代码如下所示:
//hasAttribute接受QString参数
if(node.hasAttribute(“http contents length”)//。。。
则将创建临时QString以作为hasAttribute函数参数传递。这可能非常昂贵,因为它涉及到内存分配和将数据复制/转换为QString的内部编码。
可以通过使用QStringLiteral来避免这种成本:
if(node.hasAttribute(QStringLiteral(u“http contents length”))//。。。
在这种情况下,QString的内部数据将在编译时生成;运行时不会发生转换或分配。
使用QStringLiteral而不是双引号的纯C++字符串literal可以显著加快从编译时已知的数据创建QString实例的速度。
如果你在程序中像这样QString("test")或QString test = "test"编写代码的时候,都会调用QString的构造函数:
inline QString(QLatin1StringView latin1);
QLatin1StringView是const char*字符串的简单封装,从下面的源码可以看出:
constexpr inline explicit QLatin1String(const char *s) noexcept
: m_size(s ? qsizetype(QtPrivate::lengthHelperPointer(s)) : 0), m_data(s) {}
QLatin1StringView的构造函数之一。
从QLatin1StringView到QString, 会有一个编码的转换和QString的构造过程,而且这个过程都是在程序的运行过程中发生的,这势必会降低程序的运行效率,于是QStringLiteral就产生了,下面就从源码一步步分析是如何实现高效的。
下面的分析都是基于Qt6.5.2,mingw 64位源码下进行的。
unicode编码的字符串宏:
#define QT_UNICODE_LITERAL(str) u"" str
QStringPrivate的生成函数:
using QStringPrivate = QArrayDataPointer;
namespace QtPrivate {
template
static Q_ALWAYS_INLINE QStringPrivate qMakeStringPrivate(const char16_t (&literal)[N])
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
auto str = const_cast(literal);
return { nullptr, str, N - 1 };
}
}
QArrayDataPointer的结构为:
template
struct QArrayDataPointer
{
private:
typedef QTypedArrayData Data;
typedef QArrayDataOps DataOps;
....
public:
Data *d;
T *ptr;
qsizetype size;
}
QTypedArrayData是Qt很多容器内部管理内存的数据结构,如QByteArray
、QVector
等多个类都使用它来管理连续内存。
而QString内部存放数据的数据类型正是QStringPrivate
class Q_CORE_EXPORT QString
{
typedef QTypedArrayData Data;
public:
typedef QStringPrivate DataPointer;
...
explicit QString(DataPointer &&dd) : d(std::move(dd)) {}
...
DataPointer d;
}
#define QStringLiteral(str) \
(QString(QtPrivate::qMakeStringPrivate(QT_UNICODE_LITERAL(str)))) \
/**/
宏是在编译的时候展开替换,具体的展开替换流程如下:
这几个关键的流程都是在编译的时候发生的,不占用运行时间,因此利用QStringLiteral宏传字符串比直接传字符串效率高很多。
void testQStringLiteral(const QString& str)
{
//qDebug() << "QString" << str;
}
void test()
{
const auto nLoopTimes = 1E7;
QElapsedTimer t;
t.start();
for (auto i = 0; i < nLoopTimes; ++i)
{
testQStringLiteral("Literal");
}
int elapsed1 = t.elapsed();
qDebug() << "Literal:" << t.elapsed();
QElapsedTimer t1;
t1.start();
for (auto i = 0; i < nLoopTimes; ++i)
{
testQStringLiteral(QStringLiteral("Literal"));
}
int elapsed2 = t1.elapsed();
qDebug() << "QStringLiteral:" << t1.elapsed();
}
测试结果输出:
Literal: 12568 ms
QStringLiteral: 668 ms
通过上面的源码分析之后,你们能更好的理解什么时候用和不用QStringLiteral。