繁体   English   中英

如何在头文件中声明静态const char *?

[英]How to declare a static const char* in your header file?

我想在头文件中为我的.cpp文件定义一个常量char *。 所以我试过这个:

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

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

错误C2864:'SomeClass :: SOMETHING':只能在类中初始化静态const积分数据成员

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

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

在你的标题中:

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 integer或const枚举类型,则它在类定义中的声明可以指定一个常量初始化器,它应该是一个整型常量表达式。 在这种情况下,成员可以在其范围内出现在整数常量表达式中。 如果在程序中使用该成员,并且名称空间范围定义不包含初始化程序,则该成员仍应在名称空间作用域中定义。

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

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

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

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

错误是您无法在类中初始化static const char* 您只能在那里初始化整数变量。

您需要在类中声明成员变量,然后在类外部初始化它:

//头文件

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

// cpp文件

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

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

相反, char*变量指向内存中的实际对象,这是实际存在所必需的,并且它是使对象存在的定义(包括初始化)。 “一个定义规则”意味着您因此不希望将其放在标题中,因为包含该标题的所有翻译单元都将包含该定义。 它们无法链接在一起,即使字符串在两者中都包含相同的字符,因为在当前的C ++规则下,您已经定义了两个具有相同名称的不同对象,这是不合法的。 他们恰好在其中具有相同的字符这一事实并不能使其合法化。

使用C ++ 11,您可以使用constexpr关键字并在标题中写入:

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


笔记:

  • constexpr使SOMETHING成为一个常量指针,因此您无法编写

     SOMETHING = "something different"; 

    稍后的。

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

     constexpr const char* MyClass::SOMETHING; 
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;
   }
};

如果您使用的是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将在多个目标文件中声明,链接器也只会拾取一个。

请记住,这只适用于Microsoft工具链。 不要指望这是便携式的。

您可以使用模板提供仅限H文件常量的技巧。

(注意,这是一个丑陋的例子,但至少在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允许的常量初始化程序仅用于整数或枚举类型。 有关详细信息,请参见9.4.2 / 4:

如果静态数据成员是const integer或const枚举类型,则它在类定义中的声明可以指定一个常量初始化器,它应该是一个整型常量表达式(5.19)。 在这种情况下,成员可以出现在整数常量表达式中。 如果在程序中使用该成员,并且名称空间作用域定义不包含初始化程序,则该成员仍应在名称空间作用域中定义。

和9.4.2 / 7:

静态数据成员的初始化和销毁​​与非本地对象完全相同(3.6.2,3.6.3)。

所以你应该在cpp文件中写一下:

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

为了回答问题的原因 ,整数类型是特殊的,因为它们不是对已分配对象的引用,而是复制和复制的值。 这只是在定义语言时做出的一个实现决策,即处理对象系统之外的值,并以尽可能高效和“内联”的方式处理。

这并没有完全解释为什么它们被允许作为一个类型的初始化器,但是把它想象成一个#define然后它作为类型的一部分而不是对象的一部分是有意义的。

暂无
暂无

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

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