我能在头文件中#define QStringLiterals吗?

时间:2016-04-05 10:50:47

标签: c++ qt

看起来C ++编译器允许我在C ++头文件中#define QStringLiterals。类似的东西:

// foo.h
#ifndef FOO_H
#define FOO_H

#define BAR QStringLiteral("bar")

#endif

目标是在包含BAR的不同.cpp文件中使用(复制)foo.h,这样我就可以避免多次输入QStringLiteral("bar")

可是:

  • 安全吗? (我听说QStringLiteral会导致崩溃)
  • 效率如何? (即只分配一次的字符串?)

2 个答案:

答案 0 :(得分:5)

不要这样做。

关于你的问题:

  • 安全吗? (我听说QStringLiteral会导致崩溃)

问题是为什么 QStringLiteral可能崩溃,答案与库和插件卸载有关。 QStringLiteral创建一个QString,它引用静态的有效负载(QString自己的数据和实际的字符串)。如果在库A中创建此QString,传递给库B,然后卸载库A,则B将留下危险的悬空指针。 Qt在这里无能为力。

  • 效率如何? (即只分配一次的字符串?)

完全没有:

  1. 您最终会在使用它的任何地方出现字符串文字数据。这可能会破坏数据大小,或者对链接器施加更大压力,以便在多个TU中合并相同的字符串文字数据。
  2. 由于QStringLiteral扩展为lambda表达式(...给出了不错的编译器),因此每次出现宏时,都会对编译器施加压力以解析并生成lambda表达式的代码
  3. 由于每个lambda必须传达一个唯一类型的对象,因此多个宏扩展 - 即使在相同的TU中 - 也不能被编译器轻松合并。直到最近,优化器才开始折叠相同的二进制代码。
  4. 解决方案:写一个离线函数:

    // foo.h
    #ifndef FOO_H
    #define FOO_H
    
    QString bar();
    
    #endif
    

    // foo.cpp
    #include "foo.h"
    QString bar() {
        return QStringLiteral("bar");
    }
    

答案 1 :(得分:1)

  

我可以#define头文件中的QStringLiterals吗?

你可以,但你不应该,正如其他答案中所解释的那样。

另一种方法是使用全局字符串常量,使用可区分的前缀来避免全局命名空间污染。您也可以将它们放入命名空间:

// bar.h
#pragma once
#include <QString>

extern const QString kBar; // approach 1
namespace K {
  extern const QString bar; // approach 2
}

// bar.cpp
#include "bar.h"

const QString kBar { QStringLiteral("bar"); } 
const QString K::bar { QStringLiteral("bar"); }

// main.cpp
#include "bar.h"
int main() {
  qDebug() << kBar << K::bar;
}

如果您担心初始化顺序,那么在输入main()之前,只需假定值未定义;让main()显式实例化依赖于这些常量的所有内容。你应该以这种方式编码,所以这在实践中不应该是一个问题。