繁体   English   中英

静态常量字符串(类成员)

[英]Static constant string (class member)

我想要一个类的私有静态常量(在本例中是一个形状工厂)。

我想要那种东西。

class A {
   private:
      static const string RECTANGLE = "rectangle";
}

不幸的是,我从 C++ (g++) 编译器中得到了各种错误,例如:

ISO C++ 禁止初始化成员“RECTANGLE”

非整数类型“std::string”的静态数据成员的类内初始化无效

错误:使“矩形”静态

这告诉我这种成员设计不符合标准。 您如何在不必使用#define 指令的情况下拥有私有文字常量(或者可能是公共的)(我想避免数据全局性的丑陋!)

任何帮助表示赞赏。

您必须在类定义之外定义静态成员并在那里提供初始化程序。

第一的

// In a header file (if it is in a header file in your case)
class A {   
private:      
  static const string RECTANGLE;
};

接着

// In one of the implementation files
const string A::RECTANGLE = "rectangle";

您最初尝试使用的语法(类定义中的初始化程序)仅允许用于整数和枚举类型。


从 C++17 开始,您有另一个选项,它与您的原始声明非常相似:内联变量

// In a header file (if it is in a header file in your case)
class A {   
private:      
  inline static const string RECTANGLE = "rectangle";
};

不需要额外的定义。

在 C++11 中,您现在可以执行以下操作:

class A {
 private:
  static constexpr const char* STRING = "some useful string constant";
};

在类定义中,您只能声明静态成员。 它们必须在类之外定义 对于编译时整数常量,标准规定您可以“初始化”成员。 不过,这仍然不是一个定义。 例如,如果没有定义,获取地址是行不通的。

我想提一下,我看不到使用 std::string 而不是 const char[] for constants的好处。 std::string 很好,但它需要动态初始化。 所以,如果你写类似

const std::string foo = "hello";

在命名空间范围内,foo 的构造函数将在 main 开始执行之前运行,并且此构造函数将在堆内存中创建常量“hello”的副本。 除非你真的需要 RECTANGLE 成为一个 std::string 你也可以写

// class definition with incomplete static member could be in a header file
class A {
    static const char RECTANGLE[];
};

// this needs to be placed in a single translation unit only
const char A::RECTANGLE[] = "rectangle";

那里! 没有堆分配,没有复制,没有动态初始化。

干杯,S。

在 C++ 17 中,您可以使用内联变量

class A {
 private:
  static inline const std::string my_string = "some useful string constant";
};

请注意,这与abyss.7 的答案不同:这个定义了一个实际的std::string对象,而不是const char*

这只是额外的信息,但如果您真的想要头文件中的字符串,请尝试以下操作:

class foo
{
public:
    static const std::string& RECTANGLE(void)
    {
        static const std::string str = "rectangle";

        return str;
    }
};

虽然我怀疑这是推荐的。

类静态变量可以在标头中声明,但必须在 .cpp 文件中定义 这是因为静态变量只能有一个实例,编译器无法决定将它放在哪个生成的目标文件中,因此您必须做出决定。

为了在 C++11 中的声明中保留静态值的定义,可以使用嵌套的静态结构。 在这种情况下,静态成员是一个结构,必须在 .cpp 文件中定义,但值在标头中。

class A
{
private:
  static struct _Shapes {
     const std::string RECTANGLE {"rectangle"};
     const std::string CIRCLE {"circle"};
  } shape;
};

整个静态结构不是初始化单个成员,而是在 .cpp 中初始化:

A::_Shapes A::shape;

这些值通过以下方式访问

A::shape.RECTANGLE;

或 -- 因为成员是私有的,并且只能从 A 中使用 -- 与

shape.RECTANGLE;

请注意,此解决方案仍然存在静态变量初始化顺序的问题。 当使用静态值初始化另一个静态变量时,第一个静态变量可能尚未初始化。

// file.h
class File {
public:
  static struct _Extensions {
    const std::string h{ ".h" };
    const std::string hpp{ ".hpp" };
    const std::string c{ ".c" };
    const std::string cpp{ ".cpp" };
  } extension;
};

// file.cpp
File::_Extensions File::extension;

// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };

在这种情况下,静态变量将包含 { "" } 或 { ".h", ".hpp" },具体取决于链接器创建的初始化顺序。

正如@abyss.7 所述,如果可以在编译时计算变量的值,您也可以使用constexpr 但是如果你用static constexpr const char*声明你的字符串并且你的程序使用std::string否则会有开销,因为每次使用这样的常量时都会创建一个新的std::string对象:

class A {
public:
   static constexpr const char* STRING = "some value";
};
void foo(const std::string& bar);
int main() {
   foo(A::STRING); // a new std::string is constructed and destroyed.
}

要使用该类内初始化语法,常量必须是由常量表达式初始化的整型或枚举类型的静态常量。

这是限制。 因此,在这种情况下,您需要在类之外定义变量。 参考@AndreyT的答案

您可以选择上面提到的const char*解决方案,但是如果您一直需要字符串,那么您将有很多开销。
另一方面,静态字符串需要动态初始化,因此如果你想在另一个全局/静态变量的初始化过程中使用它的值,你可能会遇到初始化顺序的问题。 为避免这种情况,最便宜的方法是通过 getter 访问静态字符串对象,它会检查您的对象是否已初始化。

//in a header  
class A{  
  static string s;   
public:   
  static string getS();  
};  
//in implementation  
string A::s;  
namespace{  
  bool init_A_s(){  
    A::s = string("foo");   
    return true;  
  }  
  bool A_s_initialized = init_A_s();  
}  
string A::getS(){      
  if (!A_s_initialized)  
    A_s_initialized = init_A_s();  
  return s;  
}  

请记住只使用A::getS() 因为任何线程只能由main()启动,并且A_s_initializedmain()之前初始化,所以即使在多线程环境中也不需要锁。 A_s_initialized默认为 0(在动态初始化之前),因此如果在 s 初始化之前使用getS() ,则可以安全地调用 init 函数。

顺便说一句,在上面的答案中:“ static const std::string RECTANGLE() const ”,静态函数不能是const ,因为如果有任何对象(没有 this 指针),它们就不能改变状态。

可能只是这样做:

static const std::string RECTANGLE() const {
    return "rectangle";
} 

或者

#define RECTANGLE "rectangle"

当前标准仅允许对静态常量整数类型进行此类初始化。 因此,您需要按照 AndreyT 的解释进行操作。 但是,这将在下一个标准中通过新的成员初始化语法提供。

快进到 2018 年和 C++17。

  • 不要使用 std::string,使用 std::string_view 文字
  • 请注意下面的“constexpr”。 这也是一种“编译时”机制。
  • 没有内联并不意味着重复
  • 为此不需要任何 cpp 文件
  • static_assert 仅在编译时“有效”

     using namespace std::literals; namespace STANDARD { constexpr inline auto compiletime_static_string_view_constant() { // make and return string view literal // will stay the same for the whole application lifetime // will exhibit standard and expected interface // will be usable at both // runtime and compile time // by value semantics implemented for you auto when_needed_ = "compile time"sv; return when_needed_ ; }

    };

以上是一个适当且合法的标准 C++ 公民。 它可以很容易地参与任何和所有 std:: 算法、容器、实用程序等。 例如:

// test the resilience
auto return_by_val = []() {
    auto return_by_val = []() {
        auto return_by_val = []() {
            auto return_by_val = []() {
return STANDARD::compiletime_static_string_view_constant();
            };
            return return_by_val();
        };
        return return_by_val();
    };
    return return_by_val();
};

// actually a run time 
_ASSERTE(return_by_val() == "compile time");

// compile time 
static_assert(
   STANDARD::compiletime_static_string_view_constant() 
   == "compile time" 
 );

享受标准 C++

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM