编码过程中,会不可避免地涉及到字面字符串,很多时候大家都是直接使用,没有太多考虑转换和效率的问题。
如果调用的函数支持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或者隐式转换吧