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的創建。
原創造福大家,共享改變世界
獻出一片愛心,溫暖作者心靈
