轉載 原作者: Olivier Goffart http://woboq.com/blog/qstringliteral.html 譯者:zzjin http://www.tuicool.com/articles/6nUrIr
QStringLiteral 是Qt5中新引入的一個用來從“字元串常量”建立QString對象的宏(字元串常量指在源碼中由”"包含的字元串)。在這篇部落格我講解釋它的的内部實作和工作原理。
提要
讓我們從它的使用環境開始說起: 假設你想要在Qt5中從字元串常量初始化一個QString對象,你應該這樣:
- 大多數情況:使用QStringLiteral(“某字元串”) --如果它最終轉會換成QString的話
- 使用QLatin1String(“某字元串”) --如果使用的函數有支援QLatin1String的重載(比如operator==, operator+, startWith, replace等)的話
我把這段話放在最開始是為了那些不怎麼想了解其具體技術細節的人着想。
繼續閱讀你将了解QStringLiteral是如何工作的。
回顧QString的工作方式
QString,和Qt中的其他類一樣,是一個”隐式共享類“。它唯一的資料成員就是一個指向其“私有”資料的指針。 QStringData由 malloc函數配置設定空間,并且在其後(同一塊記憶體塊)配置設定了足夠的空間來存放實際的字元資料。
|
offset是指向QStringData相對資料的指針。在Qt4中它是一個實際的指針。稍後我們會講到為什麼這個指針發生了變化。
在字元串中儲存的實際資料是UTF-16編碼的,這意味着每一個字元都占用了兩個位元組。
文字與轉換
字元串常量是指直接在源碼中用引号包起來的字元串。
這有一些例子。(假設action,string和filename都是QString類型)
|
第一行我們調用了
QObject::setObjectName(const QString&)函數。
這裡有一個通過構造函數産生的從const char*到QString的隐式轉換。一個新的QStringData擷取了足夠儲存 "MyObject"字元串的空間,接着這個字元串 從 UTF-8轉碼為UTF-16并拷貝到Data内 。
在最後一行調用QString::replace(const QString &, const QString &)函數的時候也發生了相同的操作,一個新的QStringData擷取了儲存 "%FileName%"的空間。
有辦法避免QStringData的記憶體配置設定和字元串的複制操作嗎?
當然有,建立臨時的QString對象耗費甚巨,解決這個問題的一個方法是重載一個
const char*作為參數的通用方法。 于是
我們有了下面的這幾個指派運算符重載:
|
這些重載運算可以直接操作原始char*,不必為了我們的字元串常量去建立臨時QString對象。
編碼與 QLatin1String
在Qt5中,我們把char* 字元串的預設編碼 改成了UTF-8。但是相對純ASCII或者latin1而言,很多算法處理UTF-8編碼資料的時候會慢很多。
是以你可以使用QLatin1String,它是在确定編碼的情況下對char*進行的輕量級封裝。一些接收QLatin1String為參數的重載函數能夠直接對純latin1資料進行處理,不必進行編碼轉換。
是以我們的第一個例子現在看起來是這樣了:
|
好消息是QString::replace與operator==操作有了針對QLatin1String的重載函數,是以現在快很多。
在對s etObjectName的調用中,我們避免了從UTF-8的編碼轉換,但是我們仍然需要進行一次從QLatin1String到QString的(隐性)轉換, 是以不得不堆中配置設定QStringData的空間。
介紹 QStringLiteral
QStringLiteral
有沒有可能在調用setObjectName的時候同時阻止配置設定空間與複制字元串常量呢?當然,這就是
QStringLiteral所做的。
這個宏會在編譯時嘗試生成QStringData,并初始化其全部字段。它甚至是存放在.rodata記憶體段 中是以可以在不同的程序中共享。
為了實作這個目标我們需要兩個C++語言的特性:
-
在編譯的時候生成UTF-16格式字元串的可能性
Win環境下我們可以使用寬字元
Unix環境下我們使用新的C++11 Unicode字元串:L"String"。
GCC 4.4和clang支援。)u"String"。(
-
從表達式中建立靜态資料的能力
我們希望能把QStringLiteral放在代碼的任何地方。一種實作方法就是把一個靜态的QStringData放入一個C++11 lambda 表達式。(MSVC 2010和GCC 4.5支援) (我們同樣用到了GCC
)__extension__ ({ })
實作
我們需要一個同時包含了QStringData和實際字元串的POD結構。這個結構取決于我們生成的UTF-16時使用的實作方法。
|
|
讓我們稍微簡化一下這個宏,然後看看這個宏是如何展開的
|
引用計數器初始化為-1。由于這是隻讀資料是以這個負數永遠不會發生增減。
可以看到,我們使用一個偏移量(qptrdiff)而不是向Qt4中那樣使用一個指向字元串的指針是多麼重要。把一個指針放在一個隻讀的部分裡面是完全不可能的,因為指針很可能會在加載時 重新配置設定 。這意味着每次啟動或者調用程式、庫檔案的時候作業系統都不得不用重配置設定表重寫全部的指針位址。
資料結果
為了好玩,我們來看一段從一個非常簡單的對QStringLiteral的調用後生成的彙編代碼。 可以看到下面幾乎沒有什麼代碼,還有.rodata段的資料分布。
|
在x84_64用g++ -O2 -S -std=c++0x (GCC 4.7)編譯後
|
結論
我希望讀完這篇部落格的現在,你們能更好的了解什麼時候用和不用QStringLiteral。
還有一個宏叫做QByteArrayLiteral,工作原理和QStringLiteral幾乎一模一樣但是建立的是QByteArray。