0

在实践中,单例模式是使用返回一个局部静态变量的简单静态函数创建的。只要实例是静态的,它就会返回在第一次函数调用期间定义的相同变量。

对我来说令人困惑的部分是,如果我在一个头文件中声明带有静态局部变量的普通静态函数,并在调用该函数时将该头包含在两个不同的翻译单元中,则函数局部静态变量将被构造两次 - 每个翻译单元都构造。

原因是静态函数标识符函数链接是内部的,因此每个翻译单元(源文件)都有该函数的两个实例,因此该静态变量有两个本地实例。

我的问题是为什么同样的逻辑不适用于单例模式?当我们声明静态函数时,为什么它没有内部链接,因此为什么不创建局部静态变量的两个实例(根据定义,它是唯一的单例实例)?

我正在谈论的单例主要功能:

static className& instance() { static className instance; return instance; }
4

2 回答 2

1

因为static [dcl.stc]/4并不总是意味着内部链接。当应用于正常的命名空间范围函数时,例如

static void fun();  // fun has internal linkage

说明static符声明此函数具有内部链接[basic.link]/5。这主要是为了与 C 向后兼容。在 C++ 中,您最好使用未命名的命名空间来声明具有内部链接的实体,以避免导致您的问题的那种混淆:

namespace
{
    void fun();  // fun has internal linkage, C++ style
}

当应用于类的成员函数时,说明static符将该函数声明为该类的static成员函数,即不对该类的实例进行操作但只是在该类范围内声明的普通函数的函数, 例如:

class X
{
public:
    static void fun();  // fun is a static member function with external linkage
};

void test()
{
    X::fun();  // invoke fun
}

非命名空间范围函数(例如静态成员函数)的链接不受关键字的影响static。结果,上面的静态成员函数将具有外部链接[basic.link]/6

除此之外:Singleton 模式几乎肯定不会成为您想要做的事情的正确选择。不要这样做。

于 2019-04-24T18:18:07.463 回答
-1

返回单例的函数不应该是静态的,只有单例本身。

这是不正确的

//header file - bad implementation of singleton pattern

class foo {/*stuff*/};

static foo& getFoo() {
    static foo myFoo;
    return myFoo;
}

此实现将导致在myFoo每个编译单元中返回不同的结果,因为每个编译单元都有自己的getFoo函数

你应该做的是:

//header file - good implementation of singleton pattern

class foo {/*stuff*/};

foo& getFoo();

//source file - good implementation of singleton pattern

foo& getFoo() {
    static foo myFoo;
    return myFoo;
}

现在每个编译单元将引用相同的getFoo函数并获得相同的单例(因为函数不是静态的)


作为一个可测试的例子来说明这一点。

//foo.h
#include <iostream>

static void print_num_times_called() {
    static int num_times_called = 0;
    ++num_times_called;
    std::cout << num_times_called << "\n";
}

void call_print_num_times_called();

//foo.cpp
#include "foo.h"

void call_print_num_times_called() {
    print_num_times_called();
}

//main.cpp
#include "foo.h"

void main() {
    for (int i = 0; i < 10; ++i) {
        print_num_times_called();
    }
    for (int i = 0; i < 10; ++i) {
        call_print_num_times_called();
    }
}

//output

1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
于 2019-04-24T18:12:21.773 回答