c – 为什么从内联类方法中访问的静态名称空间变量可以工作?

我最近在头文件中看到这个代码,令人惊讶的是它的工作:
namespace NS {
  static int uid = 0;
  class X {
   public:
    static int getUID() { return uid++; }
  };
}

如果从几个不同的C源文件调用静态方法NS :: X :: getUID(),我很惊讶地发现它正确地生成了唯一的ID(跨翻译单元).我认为命名空间范围中的一个静态变量与翻译单元有内部链接.这里发生了什么?类X中的内联静态方法是否具有自己的翻译单元,这就是为什么它生成唯一的ID?或者是因为我的编译器有点怪怪,这对我来说有用吗?

上述代码是否依赖于“安全”的明确行为?如果是这样,这是一个令人惊讶的简洁的方法生成内联或模板类中的唯一ID,即使它看起来有点kludgy.或者更好地为这样的静态唯一ID函数生成一个新的C源文件,并在类中移动静态ID?

更新:

对于一个测试用例,我在不同的文件(file1.cpp,file2.cpp等)中写了几个这样的功能

#include "static_def.h" // Name of the above header file.
void func1() {
  int uid1 = NS::X::getUID();
  int uid2 = NS::X::getUID();
  std::cout << "File1,UID1: " << uid1 << ",UID2: " << uid2 << std::endl;
}

令人惊讶的输出(从主要调用这些输出后)是:

File1,UID1: 0,UID2: 1
File2,UID1: 2,UID2: 3

解决方法

您的代码只有在您将此标题包含在单个源文件中时才正确.

因为uid被声明为静态,它具有内部链接.每个源文件都有一个uid变量的实例,其中包含此头.如果将这个头文件包含在三个源文件中,那么会有三个uid变量.

getUid函数是隐式内联的,因为它定义在类定义的内部.它是静态成员函数的事实是无关紧要的.内联函数的规则是必须在其使用的每个源文件中定义内联函数,并且所有定义必须相同.

您的getUid函数定义违反了这个规则:是的,它在每个包含头文件的源文件中定义(因为它在头中定义),但每个定义是不同的,因为在每个定义中,引用的uid是一个不同的uid变量.

因此,您的程序违反“一维规则”,并表现出未定义的行为.您观察到的特定行为可能是因为编译器选择了内联函数一个定义,并抛弃了其余的内容,所以您认为您正在使用的“全局变量”恰好恰好是uid变量之一 – 无论哪个被引用通过编译器保存的getUid的副本.虽然这是这种形式的未定义行为的典型表现,但行为仍未定义.

您可以使uid变量function-local来确保只有一个实例,并避免违反一个定义规则:

namespace NS {
  class X {
  public:
    static int getUID() {
      static int uid = 0;
      return uid++;
    }
  };
}

在这种情况下,在程序中将只有一个uid实例被保证.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...