【问题标题】:How to work with variable in namespace如何使用命名空间中的变量
【发布时间】:2012-07-13 18:09:43
【问题描述】:

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

test.h:

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

main.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 被多次声明,有人可以解释为什么会这样吗?

谢谢

【问题讨论】:

  • 您知道#include 的工作原理吗?它复制粘贴指定文件的内容。 setNum 的定义真的也在 main.cpp 中吗?
  • 我没有展示它,但我的 .h 中有标准的 ifNDEF DEF。此外,由于我只在整个程序中包含它一次,它仍然不是问题,对吧?
  • 你使用using namespace test吗?
  • 你确定只包含一次?
  • 请出示真实代码。如果没有包含test.h 的其他文件,您将不会收到多个定义错误。 cout&lt;&lt;testNum 也应该是 cout&lt;&lt;test::testNum

标签: c++ static namespaces


【解决方案1】:

首先,您的误解与命名空间无关,只是关于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,您在包含test.h 的每个文件中都有一个名为testNum不同 变量。这意味着,如果您在一个文件中设置 testNum 并在另一个使用 testNum 的文件中调用函数,则它引用了一个 不同 变量,而该变量恰好具有相同的名称。

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

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

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

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

int testNum = 1;

【讨论】:

  • 谢谢,这解释了我的问题。正如我认为我在我的 java 开发中对 static 如何工作做出了错误的假设,尽管我首先查看了 static 并认为 c++ static 是相同的......在那些情况下,你看到了你所期望的答案。我真的希望我的真实程序无论如何都将静态值默认为安全的值(即使有人去更改我的 main.h),以确保我没有奇怪的功能。似乎 extern 阻止我让那个 gaurentee 以有效的默认值开头。有什么办法可以保证?
  • static 在 C++ 中意味着几件事。对于类成员,它类似于Java,但你的程序中没有类成员,你有非成员全局变量,Java 中不存在。
  • 我不知道你所说的 extern 防止“默认”值或“默认为安全的东西”是什么意思。我在回答中给出的 = 1 值有什么问题?
  • 我不需要在某个方法中完成 testNum=1 吗?绝对可行,但感觉不安全,如果有人不小心删除了某处函数中的那一行,因为他们没有意识到代码做了什么坏事。在我的真实代码中,我正在使用函数指针,如果指针不是从 .SO 加载的,我希望默认为无操作方法,而且我知道无论如何我都会感到更安全,即使有人玩我的main 函数稍后,如果没有加载其他内容,我总是指向我的 no-op 函数。诚然,这是一件小事。
  • 不,您在命名空间范围内定义它。这不是设置变量,它提供了您声明为extern 的变量的定义。 extern 声明是对编译器的承诺,即在程序中的某个地方会有一个定义……所以你必须在某个地方定义它。但是,对于任何全局变量,您都需要注意 static initialization order fiasco(在这种情况下,“静态”是指全局变量的生命周期,而不是使用 static 限定符的实体)
【解决方案2】:

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

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 并且确实 没有有初始化器。

【讨论】:

  • 感谢您的解决方案(我会在一秒钟内进行测试)但是对于我自己的启发,您能否更好地解释问题所在?你说命名空间中的所有变量都是静态的,但这仍然让我想知道为什么在这个例子中我的代码不起作用,因为我只需要一个静态变量。你是否坚持认为我最终在我引用 testnum 的每个地方都声明了两个具有相同名称的静态变量?
【解决方案3】:

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

我复制/粘贴并修正了您的拼写错误,并手动添加:

#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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-03
    • 2021-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-17
    • 1970-01-01
    相关资源
    最近更新 更多