如何在头文件中声明一个静态的const char *

我想在我的头文件中定义一个常量字符* .cpp文件来使用。 所以我试过这个:

private: static const char *SOMETHING = "sommething"; 

这给我带来了以下编译器错误:

错误C2864:'SomeClass :: SOMETHING':只有静态常量整型数据成员可以在类中初始化

我是C ++新手。 这里发生了什么? 为什么这是非法的? 你怎么能做到呢?

您需要在翻译单元中定义静态variables,除非它们是整型。

在你的标题中:

 private: static const char *SOMETHING; static const int MyInt = 8; // would be ok 

在.cpp文件中:

 const char *YourClass::SOMETHING = "something"; 

C ++标准,9.4.2 / 4:

如果静态数据成员是const整型或常量枚举types,那么它在类定义中的声明可以指定一个常数初始值设定项,它应该是一个整型常量expression式。 在这种情况下,成员可以出现在其范围内的整型常量expression式中。 如果在程序中使用成员,则该成员仍应在名称空间作用域中定义,并且名称空间作用域定义不应包含初始值设定项。

回答OP为什么只允许使用整数types的问题。

当一个对象被用作左值(即作为具有存储地址的东西)时,它必须满足“一个定义规则”(ODR),即它必须被定义在一个且只有一个翻译单元中。 编译器不能也不会决定哪个翻译单元来定义该对象。这是你的责任。 通过在某个地方定义这个对象,你不仅仅是定义它,而是实际上告诉编译器,你想在这里定义它,在这个特定的翻译单元中。

同时,在C ++语言中积分常数有特殊的地位。 它们可以形成积分常量expression式(ICE)。 在ICE中,整型常量被用作普通 ,而不是作为对象 (即这种整数值是否在存储器中有地址是不相关的)。 实际上,ICE在编译时进行评估。 为了促进这种积分常数的使用,它们的值必须是全局可见的。 常数本身并不需要真正的存储空间。 由于这个整数常量得到了特殊的处理:它允许在头文件中包含它们的初始化程序,并且提供一个定义的要求被放宽了(事实上,然后是法律上)。

其他常量types没有这样的属性。 其他常数types实际上总是用作左值(或者至less不能参与ICE或类似于ICE的任何types),这意味着它们需要定义。 其余的如下。

错误是你不能在类中初始化一个static const char* 。 你只能在那里初始化整型variables。

您需要在类中声明成员variables,然后在类之外初始化它:

//头文件

 class Foo { static const char *SOMETHING; // rest of class }; 

// cpp文件

 const char *Foo::SOMETHING = "sommething"; 

如果这看起来很烦人,可以认为是因为初始化只能出现在一个翻译单元中。 如果是在类定义中,通常会包含多个文件。 常数整数是一种特殊情况(这意味着错误消息可能不是那么清晰),编译器可以有效地用整数值replacevariables的使用。

相比之下, char*variables指向内存中的实际对象,这是真正存在的要求,它是使对象存在的定义(包括初始化)。 “一个定义规则”意味着你不想把它放在一个标题中,因为那么包括该标题的所有翻译单元都将包含该定义。 即使string中包含相同的字符,也不能将它们连接在一起,因为在当前的C ++规则下,您已经定义了两个具有相同名称的不同对象,这是不合法的。 事实上,他们碰巧具有相同的字符,并不合法。

 class A{ public: static const char* SOMETHING() { return "something"; } }; 

我一直这样做 – 特别是对于昂贵的const默认参数。

 class A{ static const expensive_to_construct& default_expensive_to_construct(){ static const expensive_to_construct xp2c(whatever is needed); return xp2c; } }; 

使用C ++ 11,你可以使用constexpr关键字并写在你的头文件中:

 private: static constexpr const char* SOMETHING = "something"; 

笔记:

  • constexpr使得SOMETHING成为一个不变的指针,所以你不能写

     SOMETHING = "something different"; 

    稍后的。

  • 根据您的编译器,您可能还需要在.cpp文件中写明确的定义:

     constexpr const char* MyClass::SOMETHING; 

如果您使用的是Visual C ++,则可以使用提示链接器进行不可移植的操作。

 // In foo.h... class Foo { public: static const char *Bar; }; // Still in foo.h; doesn't need to be in a .cpp file... __declspec(selectany) const char *Foo::Bar = "Blah"; 

__declspec(selectany)表示即使Foo::Bar会在多个目标文件中被声明,链接器也只会select一个。

请记住,这将只适用于Microsoft工具链。 不要期望这是可移植的。

有一个技巧,你可以使用模板提供H文件只有常量。

(注意,这是一个丑陋的例子,但至less在g ++ 4.6.1中是逐字的)。

(values.hpp文件)

 #include <string> template<int dummy> class tValues { public: static const char* myValue; }; template <int dummy> const char* tValues<dummy>::myValue = "This is a value"; typedef tValues<0> Values; std::string otherCompUnit(); // test from other compilation unit 

(main.cpp中)

 #include <iostream> #include "values.hpp" int main() { std::cout << "from main: " << Values::myValue << std::endl; std::cout << "from other: " << otherCompUnit() << std::endl; } 

(other.cpp)

 #include "values.hpp" std::string otherCompUnit () { return std::string(Values::myValue); } 

编译(例如,g ++ -o main main.cpp other.cpp && ./main),并看到两个引用在头中声明的常量的编译单元:

 from main: This is a value from other: This is a value 

在MSVC中,您可以改为使用__declspec(selectany)

例如:

 __declspec(selectany) const char* data = "My data"; 

C ++ Standard允许的常量初始值设定项仅用于整型或枚举types。 详情见9.4.2 / 4:

如果静态数据成员是const整型或常量枚举types的,则它在类定义中的声明可以指定一个常数初始值设定项,它应该是一个整型常量expression式(5.19)。 在这种情况下,成员可以出现在整型常量expression式中。 如果在程序中使用成员,名称空间范围定义中不包含构造器,则成员仍应在名称空间范围内定义。

和9.4.2 / 7:

静态数据成员与非本地对象(3.6.2,3.6.3)一样被初始化和销毁​​。

所以你应该写在cpp文件的某个地方:

 const char* SomeClass::SOMETHING = "sommething"; 

为了回答为什么问题,整数types的特殊之处在于它们不是对分配对象的引用,而是被复制和复制的值。 这只是在定义语言时做出的一个实现决定,这个决定是为了处理对象系统之外的值,并且尽可能高效和“内联”。

这并不能准确地解释为什么它们被允许作为一个types的初始化符,但是把它看作本质上的一个#define ,那么它将作为types的一部分而不是对象的一部分。