C++ 如何在运行时以最小的开销共享全局常量?

C++ 如何在运行时以最小的开销共享全局常量?,c++,c++11,C++,C++11,我正在使用C++11。我不允许使用boost等外部库。我只能使用STL 我有许多事件,它们必须被标识为字符串常量。我不允许使用枚举、整数或任何其他数据类型。例如: “事件名称1” “事件名称2” “一些其他事件名称3” “不同的事件名称12” 然后我有一些类需要使用这些字符串,但不知道其他类是否存在(它们之间没有任何关系) 另一类:: class SomeClass{ SomeClass(){ SomeSingleton::listenForEvent("event_n

我正在使用C++11。我不允许使用boost等外部库。我只能使用STL

我有许多事件,它们必须被标识为字符串常量。我不允许使用枚举、整数或任何其他数据类型。例如:

“事件名称1”

“事件名称2”

“一些其他事件名称3”

“不同的事件名称12”

然后我有一些类需要使用这些字符串,但不知道其他类是否存在(它们之间没有任何关系)

另一类::

class SomeClass{

    SomeClass(){
        SomeSingleton::listenForEvent("event_name");
    }

    void receiveEvent(){
         //This function is triggered when "event_name" occurs.
         //Do stuff
    }
}
所有这些事件都是常量,用于识别正在发生的事情

以下是我尝试过的:

那里的一些人建议我提供如何解决具体问题的具体细节,因此我提出了这个新问题

如何将字符串存储在公共文件中,以便使用这些字符串的所有其他类都可以引用同一文件?

  • 我不想在我的应用程序的生命周期中浪费内存或泄漏内存(它是一个移动应用程序)
  • 编译时间对我来说没什么大不了的,因为这个项目不是那么大
  • 预计可能会有50个不同的活动
  • 似乎将所有字符串保存在一个文件中,并在情况发生变化时仅编辑此文件更易于维护
  • 任何类都可以在任何时间监听任何事件,在编译之前我不知道

您可以拥有静态字符串的结构:

struct MyNames
{
    static const std::string name1;
};
在cpp中:

const std::string MyNames::name1 = "foo";

然后,您可以从所有需要的位置访问这些名称。在C++17中,您应该使用
string\u view
来避免对象构造。但这似乎是这个答案的重复,基本上:

最简单的方法是使用
char const*
常量,因为它更易于优化,并且不使用动态分配

您还可以在
postEvent
函数中使用
std::string_view
,避免动态分配。此步骤是可选的。如果您不能拥有字符串视图,并且仍然希望避免动态分配,那么请参考您的实现的SSO最大容量,并将事件名称保持在该大小以下

也考虑非STD::StrugIVIEW VIE/COM>可以作为C++ 11库进行发送,最有可能是您需要的抽象。像这样的图书馆仅仅是为了这个目的而存在的

它看起来像这样:

constexpr auto event_name1 = "event_name1";
在作为静态成员的类中,其工作方式相同:

struct Type {
    static constexpr auto event_name1 = "event_name1";
};

这最多会在可执行文件的只读静态数据中占用空间。

Global
const std::string
有一个缺点,它需要在启动期间进行处理,并创建字符串文本的副本

链接的SO answear使用
constexpr std::string\u视图
,这是一个很酷的解决方案,因为构造函数是
constexpr
,所以启动时无需执行任何操作。而且它不会创建任何副本。问题是这是C++17

使用
const char[]
(或
auto
constexpr
)是一种久经验证的解决方案。您可以将
std::string
与它进行比较,而无需任何额外开销


您可以为所有这些字符串创建头文件,并让链接器删除所有重复项。它在老C++中是这样工作的。

< P>鉴于你被C++ 11所困扰,我认为我的建议仍然站在:

#ifndef INCLUDED_EVENT_NAMES
#define INCLUDED_EVENT_NAMES

#pragma once

namespace event_names
{
    constexpr auto& event_1 = "event_1";
    constexpr auto& event_2 = "event_2";
}

#endif
定义字符串文字对象的命名引用非常简单,不需要任何额外的库,保证不会引入任何不必要的对象,对于静态分配的字符串文字对象不需要任何额外的内存,并且不会有任何运行时开销


如果您可以使用C++17,我建议您使用
std::string_view
方法,但在C++11中,我认为上述方法对于您的应用程序来说是一个很好的折衷方法。

为了正确的抽象和良好的设计,您应该定义一个事件类。此事件类将具有以下任一项:

  • 提供字符串的方法(例如
    name()
    system\u name()
  • 将运算符转换为字符串(不推荐)
  • 一个
    to_string()
    独立函数,用于处理此类事件(不推荐)
但除此之外,您的所有类现在都可以使用枚举、索引或他们喜欢的任何东西,只要在与任何需要字符串的对象交互时使用转换方法即可。因此,您的任何类都不必真正了解这些字符串本身


字符串本身可以保存在类的.cpp实现文件中,其他人不必知道它们。(除非它们实际上是在不是您的代码中定义的,但这不是您描述问题的方式。)

s/const/constepr/
?内存分配不适用于
constepr
?哈哈,是的,这是我在问题描述.struct或namespace中创建和提到的问题,这是一个品味问题。
extern
只是一个声明,需要名称空间,它是结构的点,您在cpp中声明并实现。是的,只有一个版本,因为您只有一个
name
的实现。对于标题版本,链接器的任务是删除重复项。OP没有
string\u视图
。string视图当然是可选的。这只是为了避免每次调用中的动态分配(如果名称很长)
char-const*
也可以作为函数参数使用。如果OP使用
const-std::string&
作为参数(他应该也可能会这样做),那么每次都会得到新的对象。至少对于静态的
std::string
,它只有一次。@MatthieuBrucher我不认为
std::string const&
是OP需要的正确抽象。这不是一个银色的斗牛士。清洁
#ifndef INCLUDED_EVENT_NAMES
#define INCLUDED_EVENT_NAMES

#pragma once

namespace event_names
{
    constexpr auto& event_1 = "event_1";
    constexpr auto& event_2 = "event_2";
}

#endif