类声明或定义中的静态变量?

15

我是C++的新手。
我有一个类如下:

class CodeTest
{
private:
    static const int TOTAL=100;
};

TOTAL是一个声明还是定义?

当我阅读Scott Meyer的书时,提到在实现文件中我们需要定义类似于:

const int CodeTest::TOTAL;

为什么这是必需的?


以下是必要的输入:http://jcatki.no-ip.org/fncpp/Resources - Griwes
4
@Griwes 完全没有帮助。这是一个好问题。 - Konrad Rudolph
12
恭喜您。您刚刚解释了为什么 Stack Overflow 完全没有必要。只需购买书籍即可。顺便说一句,即使在阅读了许多(好的(和糟糕的))C++书籍来解释 TUs 如何工作之后,我也花费了很长时间才理解这一点。 - Konrad Rudolph
@Griwes,与其假装受到侮辱(同时还很冒犯),为什么不试着解释一下我读错了什么呢? - Konrad Rudolph
1
在美好的周日下午引起争议,加1! - Luchian Grigore
显示剩余2条评论
4个回答

12

在头文件之外的实现文件中进行声明是必需的,否则每个包含该头文件的翻译单元都会定义自己的对象(即变量的存储空间)。

这将违反一次定义规则。其结果是,如果在一个翻译单元中更改了变量,则其他翻译单元将看不到此更改。尽管变量是常量,但取其地址也会在不同的翻译单元中产生不同的指针,因此这很重要。


而且,由于R Marthino在聊天中提到了它:在每个TU中定义具有静态存储的单独实例是可能的,但这不是我们想要的(一般情况下)! 我们希望有一个可以从所有TU访问的单个实例。 - Konrad Rudolph
@Nawaz 成员函数是完全不同的东西,它们是内联的。请看我之前的评论,它与此有些相关。C++也会为带有静态变量的类模板执行相同的操作,但这再次创建了不同的对象。 - Konrad Rudolph
@KonradRudolph:inline只是编译器的提示,它们不一定会被内联 - Nawaz
1
@Nawaz 不要啊!inline并不是给编译器的提示,它决定了链接。是的,就实际的内联而言,它是一个提示,但它也决定了链接,这样的函数不会被导出,因此也不会违反ODR。 - Konrad Rudolph
@KonradRudolph:inline 决定的链接是如何帮助这里的? - Nawaz
显示剩余8条评论

4
由于这引起了一些争议,我查看了标准,@Nawaz是对的,我错了。
9.4.2/2
如果一个静态数据成员是const整型类型[...]。如果在程序中使用该成员,则仍应在命名空间范围内定义该成员,并且命名空间范围定义不得包含初始化项。
因此,您拥有的是一个声明,并且变量被初始化为一个值。在类外部,您必须定义变量,但不需要给它赋值。
带有const整型类型的部分仅适用于这种特殊情况 - 即您可以在类内部初始化该类型,但所有static数据成员必须在外部定义。
回答问题:
无论是否需要在类外部进行定义(取决于是否使用该成员),无论在类内部有没有初始化,都只是一个声明

在类外面,你必须定义这个变量,只有在需要静态变量的地址时才需要定义它!否则你不需要定义它 :P - Mr.Anubis
@Mr.Anubis 如果你不想要有效的C++,那么你是对的。然而,标准就是这样规定的...(如果你使用它,不仅仅是它的地址) - Luchian Grigore
1
@Mr.Anubis,这远非清晰明了。在Luchian引用的段落中,“used”一词存在很大争议。因此,C++11的措辞已经进行了修改,以使其更加明确。 - Konrad Rudolph
没错,唯一的问题是为什么Stroustrup说你可以在不取地址的情况下使用它们。 - unkulunkulu
1
@KonradRudolph 他说的和他引用的不一样:/ - Mr.Anubis

1

问题的第一部分:

这行代码:static const int TOTAL=100; 是一个声明后跟着一个初始化。

TOTAL 是一个标识符。

问题的第二部分:

需要使用 const int CodeTest::TOTAL 来初始化变量。


我认为仅仅回答“是”或“不是”并不具有建设性。 - Eitan T
TOTAL不是一个变量,因为之前已经用const关键字标记了。它是一个常数,就像任何常数定义一样处理,编译器可以为其分配数据区域的空间,也可以不分配。 - Luis Colorado

0
static const int TOTAL=100; // is a declaration followed by an initialisation.

来自C++标准第3.1节:

声明将名称引入翻译单元或重新声明先前声明引入的名称。声明指定这些名称的解释和属性。

下一段陈述了,除非它在类定义中声明静态成员,否则声明就是定义:

struct X
{
    int a;         // defines a
    static int b;  // declares b
};

您可以在这里阅读更多关于定义和声明的内容:SO: 什么是定义和声明之间的区别?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接