首页 文章

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

提问于
浏览
387

我想为一个类(在这种情况下是一个形状工厂)有一个私有静态常量 .

我想要有类似的东西 .

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

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

ISO C禁止成员'RECTANGLE'的初始化非整数类型'std :: string'的静态数据成员的无效类内初始化错误:使'RECTANGLE'静态

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

任何帮助表示赞赏 .

11 回答

  • 4

    当前标准仅允许静态常量积分类型的这种初始化 . 所以你需要像AndreyT解释的那样做 . 但是,这将通过new member initialization syntax在下一个标准中提供 .

  • 1

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

    第一

    // 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";
    

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

  • 410

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

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

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

  • 4

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

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

    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 - with

    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 };
    

    在这种情况下,静态变量头将包含{“" } or { " .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.
    }
    
  • 30

    你可以选择上面提到的 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 因为它们无论如何都无法改变状态(没有这个指针) .

  • 3

    快进到2018年和C17 .

    • 不要使用std :: string,请使用std :: string_view文字

    • 请注意'constexpr'贝娄 . 这也是一个"compile time"机制 .

    • 没有内联并不意味着重复

    • 没有必要的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

  • 140

    在C 11中,您现在可以做到:

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

    这只是额外的信息,但是如果你真的想在头文件中使用字符串,请尝试以下方法:

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

    虽然我怀疑这是推荐的 .

  • 3

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

    这是限制 . 因此,在这种情况下,您需要在类外定义变量 . 请参阅@AndreyT的回答

  • 8

    内部类定义只能声明静态成员 . 它们必须在课堂之外定义 . 对于编译时积分常量,该标准使您可以"initialize"成员的例外 . 但它仍然不是一个定义 . 例如,如果没有定义,那么获取地址是行不通的 .

    我看到了使用std :: string而不是常量char char []的好处 . 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 .

  • 15

    可能就是:

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

    要么

    #define RECTANGLE "rectangle"
    

相关问题