11

我想我对命名空间和/或静态变量有一个根本的误解。但是我已经尝试过这个测试代码(手工输入,请原谅拼写错误)

测试.h:

namespace test{
   static int testNum=5;
   void setNum(int value);
}

主.cpp:

#include <test.h>

int test::setNum(int value){
   testNum=value;
}

int main(){
    test::setNum(9);
    cout<<test::testNum;
}

当我运行它时,我得到的值是 5,而不是我预期的 9。似乎我有两个 testNum 变量实例,但这似乎与 static 应该做的完全相反。我猜我错误地假设这些功能与它们的 java equivilants 相同......

如果我从我的 testNum 声明中删除静态,我还会收到一个错误,指出 testNum 被多次声明,有人可以解释为什么会这样吗?

谢谢

4

3 回答 3

34

首先,您的误解与命名空间无关,它只是关于static. 对于这个答案的其余部分,我将仅引用它,testNum因为它在命名空间中的事实是无关紧要的。

我还假设您有另一个文件,可能名为test.cpp,其中还包含test.h并定义了该setNum函数。

当声明命名空间范围内的变量或函数(即不是类成员或函数本地)时,static这意味着实体的名称在该文件的内部。形式上它具有“内部链接”,这意味着它不能通过名称引用或链接到其他文件(可以通过指针间接引用或将其作为参数传递给另一个函数。)这意味着如果有几个文件定义static int testNum然后每个文件都有自己的内部变量,名称与testNum其他文件不同(实际上一个文件可能有static int testnum,另一个可能有static double testnum,另一个文件static char* testNum,它们都是不同的,并且对于每个文件都是内部的。)如果你在 header 中放置这样的定义,然后包含 header 的每个文件都有自己的testNum.

因此,static在标题中的变量上,您每个testNum包含test.h. 这意味着如果您在一个文件中设置testNum并在另一个文件中调用一个函数,该函数使用testNum它指的是一个不同的变量,该变量恰好具有相同的名称。

因此,static在标头中声明非常量变量几乎总是错误的。

如果没有static,您将testNum在包含 的每个文件中定义变量test.h,这是不允许的:每个实体必须在您的程序中定义一次且仅一次。解决这个问题的方法是在头文件中声明变量,但不定义它,你可以通过告诉编译器变量是extern

extern int testNum;  // N.B. no "= 1" here

这告诉编译器有一个名为“外部链接”的变量testNum,所以当代码引用testNum它时,它总是意味着相同的变量(不是带有内部 linakge 的某个名称,它是每个文件中的不同实体。)在声明一个extern变量之后,它是您有责任确保在程序中的某处提供了一个确切的定义,因此您只需在一个文件中(即不在包含在多个文件中的头文件中)定义它:

int testNum = 1;
于 2012-07-13T21:12:35.487 回答
7

staticat namespace scope 用词不当,不应使用。这意味着声明为静态的实体具有内部名称绑定;换句话说,其他翻译单元中的相同名称将引用不同的实体,并且在变量定义的情况下,每个翻译单元中将有一个单独的变量实例。它对寿命没有影响。(在命名空间范围内声明或定义的所有变量都具有静态生命周期。)

static在命名空间范围内也已弃用。不要使用它。

关于在标头中声明变量:在其前面加上 extern,而不是static。如果声明了变量extern,并且没有初始化,则声明不是定义。当然,在这种情况下,您必须在某处(在单个源文件中)提供定义。类似于以下内容:

extern int testNum = 5;
int testNum = 5;
int testNum;          //  implicitly initialized with 0.

编辑:

稍微澄清一下:生命周期和名称绑定之间存在一些混淆:

  • 对象具有生命周期(自动、静态或动态——或临时或异常),并且
  • 名称绑定到实体;如果名称被声明为变量,则实体是对象。

不要关键字static与静态生命周期混淆。(函数可以是static,但函数在 C++ 中没有定义的生命周期;它们就在那里。)

关于这些的规则不是很正交。基本上,关于寿命:

  • 在命名空间范围内声明的所有变量始终具有静态生命周期,
  • 在本地范围内声明的变量具有自动生命周期,除非它们被声明static,并且
  • 在类范围内声明的变量具有包含它们的类对象的生命周期,除非它们被声明static。关于生命。

具有静态生命周期的对象在之前的某个时间出现main,并一直存在到您从main.

关于名称绑定:

  • 在命名空间范围内声明的变量具有外部名称绑定,除非它们被声明static,在这种情况下,它们具有内部名称绑定(但static不推荐使用这种用法),或者如果它们是 const,并且未声明extern
  • 在类范围内声明的变量具有外部名称绑定,即使它们已声明static,并且
  • 在块范围内声明的变量没有绑定。

最后,还有一个声明是否是定义的问题。如果它是定义,则分配内存并(或可能)初始化对象。如果它不是定义,它只是告诉编译器在声明中声明的实体(对象)在其他地方有一个定义。一般来说,变量声明是一个定义,除非它被声明extern并且没有 初始值设定项。

于 2012-07-13T20:58:09.503 回答
2

您可能希望在发布代码之前确定您的代码确实存在问题;)

我复制/粘贴并修复了您的拼写错误,并手动进行了包括:

#include <iostream>
using namespace std;

namespace test{
   static int testNum=5;
   void setNum(int value);
}

void test::setNum(int value){
   testNum=value;
}

int main(){
    test::setNum(9);
    cout<<test::testNum;
}

结果:

$ ./a.out 
9

你没有说的是你的程序中还有什么。如果您不仅有 main.cpp,还包含您的 test.h,那么每个 .cpp 文件都将拥有自己的 testNum 副本。如果您希望他们共享,那么您需要除一个以外的所有内容将其标记为extern

于 2012-07-13T21:00:04.890 回答