关于C++:静态常量字符串(类成员)

Static constant string (class member)

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

我想要类似的东西。

1
2
3
4
class A {
   private:
      static const string RECTANGLE ="rectangle";
}

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

ISO C++ forbids initialization of
member ‘RECTANGLE’

invalid in-class initialization of static data member of non-integral type ‘std::string’

error: making ‘RECTANGLE’ static

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

感谢您的帮助。


必须在类定义之外定义静态成员,并在那里提供初始值设定项。

弗斯特

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

然后

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

最初尝试使用的语法(类定义中的初始值设定项)只允许与整型和枚举类型一起使用。

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

1
2
3
4
5
// 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中,你现在可以做:

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


在类定义中,只能声明静态成员。它们必须在类外定义。对于编译时整型常量,标准会做出异常,您可以"初始化"成员。不过,这仍然不是一个定义。例如,如果没有定义,获取地址将无法工作。

我想说的是,我没有看到使用std::string代替const char[]作为常量的好处。std::string很好,但它需要动态初始化。所以,如果你写一些像

1
const std::string foo ="hello";

在命名空间范围内,foo的构造函数将在主启动执行之前运行,并且此构造函数将在堆内存中创建常量"hello"的副本。除非您真的需要矩形是一个std::string,否则您也可以编写

1
2
3
4
5
6
7
// 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";

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

干杯。


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

1
2
3
4
5
6
7
8
9
10
class foo
{
public:
    static const std::string& RECTANGLE(void)
    {
        static const std::string str ="rectangle";

        return str;
    }
};

尽管我怀疑这是推荐的。


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

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

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


To use that in-class initialization
syntax, the constant must be a static
const of integral or enumeration type
initialized by a constant expression.

这就是限制。因此,在本例中,您需要在类外部定义变量。请从@andreyt查询答案


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

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

1
2
3
4
5
6
7
8
class A
{
private:
  static struct _Shapes {
     const std::string RECTANGLE {"rectangle"};
     const std::string CIRCLE {"circle"};
  } shape;
};

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

1
A::_Shapes A::shape;

使用访问值

1
A::shape.RECTANGLE;

或者——因为成员是私有的,并且只用于

1
shape.RECTANGLE;

请注意,此解决方案仍然存在以下问题:静态变量的初始化。当静态值用于初始化另一个静态变量,第一个变量可能未初始化,然而。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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对象:

1
2
3
4
5
6
7
8
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.
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//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,因为如果有任何对象(没有此指针),它们就不能更改状态。


可能只做:

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

1
#define RECTANGLE"rectangle"


当前标准只允许对静态常量整型进行此类初始化。所以你需要按照安德烈的解释去做。但是,这将在下一个标准中通过新的成员初始化语法提供。


快速前进到2018和C++ 17。

  • 不要使用std::string,使用std::string_查看文本
  • 请注意下面的"constexpr"。这也是一种"编译时"机制。
  • 没有内联并不意味着重复
  • 不需要cpp文件
  • 仅在编译时静态断言'works'

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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::算法、容器、实用程序等。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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++