如何存储将由许多不同的类访问的字符串常量?

时间:2019-04-03 09:50:05

标签: c++ c++11

堆栈溢出有太多不同的答案:

  1. 声明一个名称空间,并在hpp文件中将所有字符串标记为extern const,并在cpp文件中放置其定义。

    C++ How to share constants with extern between cpp - Error: storage class specified

  2. 使用静态const代替extern const:

    https://softwareengineering.stackexchange.com/questions/351827/is-it-bad-practice-to-define-constants-using-class-static-methods

  3. 使用内联函数:

    static string constants in class vs namespace for constants [c++]

  4. 使用匿名名称空间!

    Where do I put constant strings in C++: static class members or anonymous namespaces?

这真是令人困惑。使用内联函数似乎是返回字符串常量的冗长而乏味的方法。

为什么我们不能只使用包含静态字符串的名称空间,这些名称空间在cpp文件中定义?

有人可以提供一个明确/明确的答案,说明应如何将字符串存储在一个常量文件中,以便多个cpp文件可以访问它们?

编辑: 在C#和Java中,将所有常量存储在“常量”文件中似乎不是问题。在C ++中最简单的方法是什么?

编辑:Java中的这些答案似乎是特定且可以理解的。在C ++中,尚不清楚哪种方法可能具有最少的编译时间,最少的运行时内存使用量。

Sharing constant strings in Java across many classes?

https://dzone.com/articles/constants-in-java-the-anti-pattern-1

4 个答案:

答案 0 :(得分:3)

这个问题太广泛了,无法回答。但是,基于对注释中给出的应用程序的性质(内部用于注册事件的名称)的进一步说明,我可能会建议使用类似于此类的标头

#ifndef INCLUDED_EVENT_NAMES
#define INCLUDED_EVENT_NAMES

#pragma once

#include <string_view>

namespace event_names
{
    using namespace std::literals;

    inline constexpr auto name1 = "value1"sv;
    inline constexpr auto name2 = "value2"sv;
}

#endif

使用std::string_view常量而不是普通的const char*const char[N]意味着您知道每个字符串的长度,而不必依赖空终止。使用std::string几乎肯定会在运行时带来内存开销和初始化成本。此处定义的std::string_view将编译为直接引用静态分配的字符串文字对象的代码。即使在多个转换单元(.cpp文件)中使用相同的常量时,现代编译器也几乎可以肯定地将相同的字符串文字在二进制文件中存储一次(由[lex.string]/15启用的标准优化)

如果您坚持使用C ++ 11,那么为所需的字符串文字对象做一些命名引用是最简单的(并且很可能足以完成您的工作)

#ifndef INCLUDED_EVENT_NAMES
#define INCLUDED_EVENT_NAMES

#pragma once

namespace event_names
{
    constexpr auto& name1 = "value1";
    constexpr auto& name2 = "value2";
}

#endif

由于引用不是对象,因此任何人都不可能做任何事情(偶然或其他方式),而这会导致为常量本身创建实际对象(当然,字符串文字对象除外)。另外,由于它是一个引用,因此它将携带有关数组大小的信息,以防万一任何东西都可以利用该信息(请注意,该大小包括终止null)。而且,由于仍然存在向const char*的隐式转换,因此您可以在需要简单的旧C字符串的任何地方使用它们。

答案 1 :(得分:1)

您需要在C ++文件中定义它们(否则您将获得多个定义错误):

strings_id.cpp

const char* TXT_TEST1 = "Test1";

您需要在头文件中声明它们,该文件将包含在您需要的位置:

strings_id.h

extern const char* TXT_TEST1;

建议使用命名空间。

class1.cpp

#include "strings_id.h"
printf(TXT_TEST1);

class2.cpp

#include "strings_id.h"
printf(TXT_TEST1);

答案 2 :(得分:1)

您不能仅在.cpp文件中定义它们,因为您希望它们的声明在其他头文件和cpp文件中可见。您不包括.cpp文件,因此编译器在编译时不会知道如何解析字符串的名称。它将把您的定义分别编译到与您在其中定义字符串的cpp文件相对应的目标文件,但是它将无法编译使用这些字符串的文件,因此将程序链接在一起。您应该使用某种方式在头文件中声明您的字符串(或访问这些字符串的方式,例如内联包装函数)。

您不能仅在头文件中没有任何其他关键字的情况下定义它们,因为它将在将从包含该头文件的.cpp编译的每个目标文件中创建它们。

因此,您应该使用一些存储类关键字来全局定义字符串。您可以在this question中读取staticextern之间的差异。

匿名命名空间将使您的字符串仅在定义它们的翻译单元(.h或.cpp文件)中可见。所以这不是您想要的。

还要注意,内联函数是隐式外部的。

编辑:

我个人只会使用extern关键字,因为使用内联包装函数没有任何意义。但是您仍然可以将它们与static结合使用,以在定义包装函数的位置定义常量。在状态定义中仍仅使用static关键字将使常量只能从定义它们的文件中访问。

答案 3 :(得分:1)

  

Sharing constant strings in Java across many classes?

如果您想要类似的东西,则可以创建头文件

#ifndef EVENT_NAME_LITERALS_HXX_
#define EVENT_NAME_LITERALS_HXX_

namespace eventnames
{
    constexpr const char* EVENT1 = "event1";
    constexpr const char* EVENT2 = "event2";
}

#endif

将其命名为test.hpp

您可以在此头文件中定义所需的常量,并在要使用的任何CPP文件中重复使用这些常量。

以下问题提供了更多信息:

use of constexpr in header file

  

constexpr暗示const和const在全局/命名空间范围内暗示   静态(内部链接),这意味着每个翻译单元   包括此标头将获得其自己的EVENT1和EVENT2副本。那个的记忆   只有在地址或引用静态时才分配静态   的地址,每次翻译的地址都会不同   单位。

     

const变量隐含的静态性专门引入到   在C ++的头文件中使用const而不是#define进行定义   常数。没有静态,将有多个符号定义   链接器错误(如果该头文件包含在多个文件中)   链接在一起的翻译单位。

如果您不想在使用此头文件的每个转换单元中拥有常量的副本,则链接器将足够聪明以优化多个副本。您也可以按照其他答案中的“外部”进行操作。