編碼過程中,會不可避免地涉及到字面字符串,很多時候大家都是直接使用,沒有太多考慮轉換和效率的問題。
如果調用的函數支持const char*這樣的參數,那么直接使用字面字符串沒有問題,這種函數一般都是極為常用的函數才會提供const char*這樣的重載,比如QString的operator==、operator+等等。
如果存在接受QLatin1String的參數,那么就可以提供QLatin1String("xxx")這樣的參數,因為QLatin1String基本上就是const char*的一層薄薄的封裝,所以這樣做的效率也是挺高的。
但是,在只接受QString參數的函數,無論我們給一個字面字符串或QLatin1String,都會隱式構造一個臨時的QString對象,構造這個對象需要在棧上申請一定的內存空間,然后把字符串拷貝過去,如果這樣的調用比較多,那還是一筆不小的開銷。此時,我們可以使用QStringLiteral來減少這個開銷。
QStringLiteral其實是一個宏,展開來就是一個lambda函數的調用,該lambda函數內部使用了一個只讀的靜態變量保存了QString對象內存布局的POD對象,讓這份數據保存在了程序的.rodata段。
當代碼運行到這的時候,實質就是對該lambda函數的調用,該函數返回了一個用上面所說的POD對象構造出來的QString對象,因為QString是隱式共享的,所以這里并沒有發生前面所說的開銷,就可以提高效率。
這里用下面的代碼展示QStringLiteral展開來之后的樣子,理解起來會更加容易:
o->setObjectName(QStringLiteral("MyObject"));
// 展開之后:
o->setObjectName(([]() {
// 計算字面字符串的大小,減去1個null字符
enum { Size = sizeof(u"MyObject")/2 - 1 };
// 編譯期就完成了這個初始化
static const QStaticStringData<Size> qstring_literal =
{ { /* ref = */ -1,
/* size = */ Size,
/* alloc = */ 0,
/* capacityReserved = */ 0,
/* offset = */ sizeof(QStringData) },
u"MyObject" };
QStringDataPtr holder = { &qstring_literal.str };
QString s(holder); // 調用QString(QStringDataPtr&)構造函數
return s;
}()) // 調用lambda函數
);
總結:
1、支持const char*或者QLatin1String的地方使用對應的參數
2、需要QString的地方,如果該QString不會修改的話,那使用QStringLiteral
3、需要QString且該QString可能會被修改的話,還是直接使用QString或者隱式轉換吧