C++静态类方法中的静态变量在头文件中的定义

7
// SomeOtherClass.hpp
#pragma once

int someOtherCallMe();

class SomeOtherClass {
  public:

    static int callMe() {
      static int _instance = 7;
      ++_instance;
      return _instance;
    }
};


// SomeOtherClass.cpp
#include "SomeOtherClass.hpp"

int
someOtherCallMe() {
  return SomeOtherClass::callMe();
}

// main.cpp

#include "SomeOtherClass.hpp"

#include <iostream>

int
main() {

  std::cout << SomeOtherClass::callMe();
  std::cout << someOtherCallMe();

  return 0;
}

我有三个文件:SomeOtherClass.hpp / cpp, main.cpp。 这些文件将导致两个二进制文件:共享库(SomeOtherClass.cpp的)和可执行文件(main.cpp的,链接到共享库)。
C ++是否保证static <any-type> _instance在程序执行期间将是单个变量(无论它在多少个二进制文件中定义)?
注意 为了澄清情况。 我在这种情况下看到的混淆是,一方面,SomeOtherClass :: callMe在程序中被定义了两次,这是预期的(因为类静态成员函数实际上是带有内部链接的常规函数,如果它们在原地定义,就像在这种情况下一样),这就是您可以从反汇编中看到的内容。由于我们在机器代码中有两个具有静态局部变量的函数。 语言/标准如何评估它们的行为?

在使用MS Visual Studio时,您需要在构建DLL和使用DLL时使用正确的__declspec才能使其正常工作。 - R Sahu
是的,我知道,谢谢。 - user14416
如果问题确实涉及跨DLL边界的静态变量,则应在标题和文本中更突出地提到。 - M.M
2个回答

2
是的,静态变量将是单个值。其他很多事情并没有被很好地定义,或者是新标准中的新内容。(如果它们是全局的话,它们何时被初始化?静态初始化的代码在函数内部是否线程安全?)但是,是的,你可以确信只有一个。
这里唯一需要澄清的是,在标准之外但在实践中非常重要的问题,如果你正在创建共享库(.so或.dll),你不能将C++类库静态(私有)链接到共享库中。否则,在两个不同的共享库中进行此操作会产生两个副本。(这个评论适用于库的所有内容,而不仅仅是静态变量。如果这样做了,那么一切都会重复。)
编辑:在许多平台上(例如Linux和Windows),这可以用来故意“隐藏”您的静态变量。如果您不让函数/类在dll / so之外可访问(使用declspec或可见性属性),那么您可以确保您的dll/so具有整个类的自己的副本。该技术有助于减少库之间不必要的交互。然而,在您的情况下,听起来您真正想要的只有一个,如果您的类在所有库中都有适当的可见性(只在一个库中可见,并且其他库链接到该库)。
再次编辑以参考标准
如果一个具有外部链接的函数在一个翻译单元中被声明为内联函数,则在出现该函数的所有翻译单元中都必须将其声明为内联函数。不需要进行任何诊断。带有外部链接的内联函数在所有翻译单元中应该具有相同的地址。 在extern inline函数中的静态局部变量总是引用同一个对象
7.1.2.4,C++14

亲爱的Rob,感谢你的努力,你的答案似乎是目前最接近我想要的,我真的不知道为什么你会被downvote,也许你在开头写了一些奇怪的东西,我实际上没有看过更改历史记录,或者可能只是粗心的stackoverflow读者。如果您能提供一些标准和/或可靠资源的参考以及这种链接是什么类型的,以及标准/语言如何评估这种构造,我将不胜感激。 - user14416
我认为我被踩是因为这是一个非常难以用是/否回答的复杂话题。在被踩之后,我还添加了澄清说明。我认为标准中没有任何内容涵盖dll和共享二进制文件。这些信息来自于一个必须管理数十个dll和大量开发人员的产品的项目负责人(即我)。我们从这些错误中吸取了教训。底线:如果它是共享的,请在某个地方定义它一次,正确导出它,编译器会处理其余部分。 - Rob L
是的,我也看到它是一个仅包含头文件的库,在其中一切都是内联的,或者是一个共享库,在其中一切都在翻译单元中定义。我知道这更清晰(哦,那个C++...))。但我只是好奇是否有东西定义了这种情况,即使不是dll /可执行文件的情况,甚至是像SomeOtherClass.hpp这样的头文件在多个翻译单元中使用,是否保证变量只有一个。请参见更新。 - user14416
如果没有dll,只有“常规”的库和目标文件,那么是的。绝对保证只有一个。 - Rob L
但混淆仍然存在,原因是什么?因为实际上会有和翻译单元一样多的 SomeOtherClass::callMe 函数。那么它如何解决静态变量,如何限定它呢?因为在正常的命名空间声明的静态变量的情况下,将会有与翻译单元一样多的变量。那么究竟什么是静态局部变量呢? - user14416
您展示了一个内联函数。该函数将出现在所有目标文件中。链接器需要将它们合并为一个实际的定义。这与旧的C语言不同,旧的C语言中,链接器会放弃并显示“错误:符号在多个目标文件中定义”。 - Rob L

1

C++是否保证在程序执行期间(无论在多少个二进制文件中定义),静态_instance将是单个变量?

我认为该语言没有关于这一点的规定。它不涉及静态库或动态库。

提供确保有一个定义的机制是实现的责任。用户需要确保使用实现提供的机制来确保函数中的static变量只有一个定义。


我同意你的观点,用户可以选择使用语言的方式来实现它想要的功能。但在这种情况下,我认为由于静态函数(SomeOtherClass::callMe)在代码中被复制了,那么静态变量也应该被复制,但至少在g++中并没有这样做。另一方面,它不应该被复制,因为正如语言所说的那样,static保证单个对象。 - user14416

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