函数内部具有静态变量的单例类(Meyer的实现)

时间:2019-07-16 15:14:13

标签: c++

Meyer的单例类蓝图在头文件temp.h中声明。如果temp.h包含在两个单独的.cpp文件中,则每个文件都有其自己的蓝图,并且由于静态内容对于其他模块(即* .o或* .cpp)不可见,因此每个.cpp文件应具有其自己的对象temp类(表示程序中temp的两个实例)。但是我已经在程序中检查了它,两个.cpp文件之间共享同一实例。我不明白为什么?

//temp.h
class temp
{
  public:
    ~temp() {}

    void temp_func()
    {
      std::cout << "Inside temp_func" << "\n";
    }
    static temp& createInstance()
    {
      static temp ins;
      return ins;
    }
  priave:
    temp() {}
};

另一个头文件,没有类实例(具有内置数据类型)

//temp1.h
inline static int& createInstance()
{
  static int ins;
  return ins;
}

//another.cpp
#include "temp1.h"
void func()
{
  int &t = createInstance();
  std::cout << "t: " << t << "\n";
  t = 20;
}

//main.cpp
#include "temp1.h"
void func();
int main()
{
  int &temp = createInstance();
  temp = 10;
  std::cout << "temp:" << temp << "\n";
  func();
  std::cout << "temp:" << temp << "\n";
  return 0;
}

程序输出

  

temp:10

     

t:0

     

temp:10

2 个答案:

答案 0 :(得分:2)

查看此inline关键字说明。报价:

  

程序中的内联函数或变量(自C ++ 17起)可能有多个定义,只要每个定义出现在不同的翻译单元中(对于非静态内联函数和变量(自C起) ++ 17))所有定义都是相同的。例如,可以在包含在多个源文件中的头文件中定义内联函数或内联变量(自C ++ 17起)。

您的createInstance函数就是这种函数-因为您已经在类定义中定义了它,所以它是隐式内联的。因此,编译器会将它们全部合并在一起,就好像它们只是一个。结果,您的函数将返回相同 ins对象。

请注意,该功能必须inline,否则会发生其他事情-确切地说,您违反了one definition rule

  

在整个程序(包括任何标准库和用户定义库)中,都需要出现每个非内联函数或变量的唯一定义(请参阅下文)。不需要编译器来诊断这种违反情况,但是未定义违反该行为的程序的行为。

编辑: 在第二种情况下,这与内置类型数据无关,而是与将createInstance函数移到类之外有关。它能做什么?它会更改您添加到函数定义中的static关键字的含义-现在该函数将被复制(而不是在链接时共享或合并在一起),因此每个翻译单元都可以int ins变量的副本。删除static关键字:

//temp1.h
inline int& createInstance()
{
  static int ins;
  return ins;
}

它正常工作:

temp:10
t: 10
temp:20

答案 1 :(得分:1)

是的,这将要发生。块范围内的静态变量(通常称为函数级静态变量),例如static temp ins;中的createInstance的行为与全局变量非常相似-即它们在所有翻译单元之间共享,并保留到程序终止为止(在他们的生命开始时有所不同,但我们暂时不会去那里。

事实上,通常,用于生成有关此类静态变量的代码的基本机制与用于通常的全局变量的机制极为相似。您甚至可以将它们视为一个以函数名称为前缀的全局变量(尽管它并不完全正确,但可能会引导您的思考过程朝着正确的方向发展)。

在这种情况下,(内联)函数中有一个静态变量,任何调用此函数的翻译单元最终都会共享相同的静态变量。