Qt6之QStringLitertal源码分析

简单介绍  

        我们先看一下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实例的速度。

用QStringLitertal的原因

        如果你在程序中像这样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位源码下进行的。

源码分析

1 先看一下几个简单的宏和类

   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很多容器内部管理内存的数据结构,如QByteArrayQVector等多个类都使用它来管理连续内存。

        而QString内部存放数据的数据类型正是QStringPrivate

class Q_CORE_EXPORT QString
{
    typedef QTypedArrayData Data;
public:
    typedef QStringPrivate DataPointer;

    ...

    explicit QString(DataPointer &&dd) : d(std::move(dd)) {}
    
    ...

    DataPointer d;
}

2 QStringLiteral宏

#define QStringLiteral(str) \
    (QString(QtPrivate::qMakeStringPrivate(QT_UNICODE_LITERAL(str)))) \
    /**/

          宏是在编译的时候展开替换,具体的展开替换流程如下:

Qt6之QStringLitertal源码分析_第1张图片

        这几个关键的流程都是在编译的时候发生的,不占用运行时间,因此利用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。       

你可能感兴趣的:(#Qt,c++,qt6.3,qt)