qt creator源碼全方面分析(4-5)


Qt中的字符串

Qt中處理字符串最常用的肯定是QString,但是在qt creator源碼中出現了大量的QLatin1String。下面我們來介紹下區別。

QLatinString

詳細介紹

我們首先來看QLatinString。類詳細介紹如下:

QString的許多成員函數都被重載以接受const char *而不是QString。 這包括復制構造函數,賦值運算符,比較運算符以及各種其他函數,例如insert(),replace()和indexOf()。 這些函數通常經過優化,以避免為const char *數據構造QString對象。 例如,假設str是QString,

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對象並進行字符數據的深拷貝。

定義QT_NO_CAST_FROM_ASCII宏(如QString文檔中所述)的應用程序無法訪問QString的const char * 接口API。為了提供一種指定常量Latin-1字符串的有效方法,Qt提供了QLatin1String,它是const char *的非常薄的包裝。使用QLatin1String,上面的示例代碼變為

  if (str == QLatin1String("auto")
          || str == QLatin1String("extern")
          || str == QLatin1String("static")
          || str == QLatin1String("register") {
      ...
  }

鍵入的時間稍長一些,但是它提供的功能與代碼的第一個版本完全相同,並且比使用QString::fromLatin1()轉換Latin-1字符串的速度更快。

多虧了QString(QLatin1String)構造函數,QLatin1String可以在需要QString的任何地方使用。 例如:

QLabel *label = new QLabel(QLatin1String("MOD"), this);

注意:如果你調用的函數,使用QLatin1String作為參數,實際上並未被重載來使用QLatin1String,而是進行QString的隱式轉換,並將觸發內存分配,而這是你通常使用QLatin1String首先要避免的情況。在這些情況下,使用QStringLiteral可能是更好的選擇。

源碼

我們看下QLatin1String源碼,進行了截取,展現核心部分

class QLatin1String
{
public:
    inline QLatin1String() : m_size(0), m_data(nullptr) {}
    inline explicit QLatin1String(const char *s) : m_size(s ? int(strlen(s)) : 0), m_data(s) {}
    
    private:
    int m_size;
    const char *m_data;
}

我們可以發現,真的是簡單的包裝,也沒有什么深拷貝什么的,只是把地址簡單的賦給了成員變量m_data。

小結

簡單說,QLatin1String就是對const char*的簡單包裝,用在QT_NO_CAST_FROM_ASCII導致無法訪問QString的const char*接口的地方。

QStringLiteral(str)

QString這個大家都很熟悉了,我們也不過多介紹。這里提一下QStringLiteral,大家可以在QString類介紹中找到。

詳細介紹

這是一個宏,在編譯時從字符串文字str中為QString生成數據。 在這種情況下,可以免費創建QString,並且將生成的字符串數據存儲在已編譯目標文件的只讀段中。

如果您的代碼如下所示:

  // hasAttribute takes a QString argument
  if (node.hasAttribute("http-contents-length")) //...

然后這將創建一個臨時QString作為hasAttribute函數參數進行傳遞。 這可能會非常昂貴,因為它涉及內存分配以及將數據復制/轉換為QString的內部編碼。

通過使用QStringLiteral可以避免此成本:

if (node.hasAttribute(QStringLiteral(u"http-contents-length"))) //...

在這種情況下,QString的內部數據將在編譯時生成。在運行時不會發生任何轉換或分配。

使用QStringLiteral而不是用雙引號引起來的純C++字符串文字,可以顯着加快根據編譯時已知的數據創建QString實例的速度。

注意:當將字符串傳遞給具有重載QLatin1String參數的函數時,QLatin1String仍比QStringLiteral更有效,並且此重載避免了轉換為QString。例如,QString::operator ==()可以直接與QLatin1String進行比較:

if (attribute.name() == QLatin1String("http-contents-length")) //...

注意:某些編譯器編碼包含US-ASCII字符集以外字符的字符串會有bug。在這種情況下,請確保在字符串前加上u。否則是可選的。

源碼

我們查看宏定義源碼

template <int N>
struct QStaticStringData
{
    QArrayData str;
    qunicodechar data[N + 1];

    QStringData *data_ptr() const
    {
        Q_ASSERT(str.ref.isStatic());
        return const_cast<QStringData *>(static_cast<const QStringData*>(&str));
    }
};

struct Q_CORE_EXPORT QArrayData
{
    QtPrivate::RefCount ref;
    int size;
    uint alloc : 31;
    uint capacityReserved : 1;

    qptrdiff offset; // in bytes from beginning of header
}

#define QStringLiteral(str) \
    ([]() Q_DECL_NOEXCEPT -> QString { \
        enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \
        static const QStaticStringData<Size> qstring_literal = { \
            Q_STATIC_STRING_DATA_HEADER_INITIALIZER(Size), \
            QT_UNICODE_LITERAL(str) }; \
        QStringDataPtr holder = { qstring_literal.data_ptr() }; \
        const QString qstring_literal_temp(holder); \
        return qstring_literal_temp; \
    }())
    
    #define Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \
    { Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset }

#define Q_STATIC_STRING_DATA_HEADER_INITIALIZER(size) \
    Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, sizeof(QStringData))

我們對Q_STATIC_STRING_DATA_HEADER_INITIALIZER宏進行替換,可以得到

#define QStringLiteral(str) \
    ([]() Q_DECL_NOEXCEPT -> QString { \
        // 計算大小,unicode為16bit一個字符,所以除2
        enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \
        // 核心,只讀靜態變量,POD結構,編譯器創建
        static const QStaticStringData<Size> qstring_literal = { \
            // QArrayData
            {-1, \
             size, \
             0, \
             0, \
             sizeof(QStringData) \
            }, \
            // qunicodechar []
            QT_UNICODE_LITERAL(str) }; \
        // 獲取編譯器創建的靜態底層數據
        QStringDataPtr holder = { qstring_literal.data_ptr() }; \
        // 構造QString,不用進行內存分配了
        const QString qstring_literal_temp(holder); \
        // 返回QString,完成加速
        return qstring_literal_temp; \
    }())

小結

說白了,QStringLiteral在編譯期就創建了數據,避免了內存分配,加速了QString的創建。


原創造福大家,共享改變世界

獻出一片愛心,溫暖作者心靈



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM