如何在头文件中声明一个静态的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的一部分而不是对象的一部分。