C++中变量的声明和定义之间有什么区别

What is the difference between declaration and definition of a variable in C++?

本文关键字:之间 什么 区别 定义 变量 声明 C++      更新时间:2023-10-16

我的问题源于Scott Meyers对Effective C++的研究。在这本书的第二项中,写了以下内容:

若要将常量的作用域限制为类,必须将其设为成员;若要确保常量最多有一个副本,必须将它设为静态成员。

这是正确的。然后立即给出以下示例:

class GamePlayer {
private:
static const int NumTurns = 5;
int scores[NumTurns];
....
};

然后写下与上述示例相关的内容:

您在上面看到的是声明,而不是NumTurns的定义。

我的第一个问题是:这句话的含义是什么

紧接着提到以下内容:

通常C++要求您为所使用的任何东西提供一个定义,但静态和整型的类特定常量(例如-integers、chars、bools)除外。只要你不记录他们的地址,你就可以在不提供定义的情况下声明和使用他们。如果你确实获取了类常量的地址,或者如果你的编译器错误地坚持定义,即使你没有获取地址,你也可以提供一个单独的定义,如下所示:const int GamePlayer::Numturns; //definition of NumTurns

为什么现在它是一个定义而不是声明

我理解函数上下文中的差异,但不理解正则变量上下文中的区别。此外,有人能详细说明作者所说的是什么意思吗

。。。如果你确实取了一个类常量的地址,或者如果你的。。上面引用的段落的一部分?

p.S:我在C++方面还是个新手。

与函数一样,变量可以有"纯声明性"声明和实际定义。您会感到困惑,因为您以前可能没有遇到过很多纯变量声明。

int i; // Definition
extern int i, j; // (Re)declares i, and declares j
extern int j = 0; // Defines j (confusing, eh?)

正如您习惯于使用函数一样,定义是声明,但并非所有声明都是定义。§3.1/2读取

声明是定义,除非[…]声明静态数据类定义中的成员(9.2,9.4),

因此,类中的静态数据成员声明永远不会定义它们声明的变量。然而,有时,变量定义并不一定存在。这就是的情况,当您可以直接使用它的值,而不必在运行时存在变量

从技术角度讲,只要静态数据成员(或任何实体)不是"odr使用的",就不必定义它所有实体的Odr使用定义见§3.2/3:

一个变量x,其名称显示为可能求值的表达式exex使用odr,除非将左值应用于右值转换(4.1)到x产生不调用的常量表达式(5.20)任何非平凡函数,如果x是对象,则ex是表达式e的一组潜在结果,其中左值到右值的转换(4.1)应用于e,或者e是废弃值表达式(第5条)。

这看起来很复杂,在标准的早期版本中更简单。然而,它粗略地说,当某个表达式"立即"访问变量值时,该变量不是该表达式所使用的odr,并且该访问生成一个常量表达式。Meyers的"获取其地址"示例只是odr使用的众多示例之一。

对于某些类别A及其静态数据成员i

class A {
static const int i = 57; // Declaration, not definition
};
const int A::i; // Definition in namespace scope. Not required per se.

这里指出:

在类接口中初始化的静态const积分数据成员不是可寻址变量。它们只是相关价值的象征性名称。由于它们不是变量,因此无法确定它们的地址。请注意,这不是一个编译问题,而是一个链接问题。在类接口中初始化的静态const变量不作为可寻址实体存在。

此处所指的"可寻址实体"是静态const数据类型的"实例"。没有实例,没有地址,也就是说,它只是一个声明
请注意,在源文件中明确定义的静态变量可以正确链接。

class X
{
public:
static int const s_x = 34;
static int const s_y;
};
int const X::s_y = 12;
int main()
{
int const *ip = &X::s_x;    // compiles, but fails to link
ip = &X::s_y;               // compiles and links correctly
} 

。。。如果你确实取了一个类常量的地址,或者如果你的。。上面引用的段落的一部分?

这意味着如果使用这样的成员,则仍然需要命名空间范围内的定义,但它不应该有初始值设定项。

struct X {
const static int n = 1;
};
const int* p = &X::n; // X::n is odr-used
const int X::n;       // ... so a definition is necessary

为什么现在它是一个定义而不是声明

因为此语句会导致编译器为静态变量生成一个地址。

此外,有人能扩展作者所说的"如果你真的接受类常量的地址":

当您使指针指向变量时,您获取其地址。

一个简短的答案是:一个声明说"这个东西存在于某个地方",而一个定义导致空间被分配。在您的案例中,您已经将其声明为static和const。编译器可能足够聪明,可以注意到,如果您只将其用作值,它可以简单地将该用法替换为文字5。它实际上不需要为某个地方的变量腾出空间并用5填充,它可以在编译时直接使用5。然而,如果你取了它的地址,编译器就不能再做出这样的假设,现在需要把5放在可寻址的地方。编译器需要它存在于一个翻译单元中(大致为:一个cpp文件。另请参阅一个定义规则。),所以现在必须在某个地方显式声明它。