在单例模式中,什么使实例独一无二?

问题描述 投票:0回答:2

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

对我来说令人困惑的部分是,如果我在一个头文件中声明具有静态局部变量的常规静态函数,并且当它们调用该函数时将该头部包含在两个不同的转换单元中,则将函数局部静态变量构造两次 - 每个用于每个转换单元。

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

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

单身主要功能我在谈论:

static className& instance() { static className instance; return instance; }
c++ compilation linker static-variables translation-unit
2个回答
1
投票

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

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

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

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

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

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模式几乎肯定不是您想要做的正确选择。 Don't do it.


-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
© www.soinside.com 2019 - 2024. All rights reserved.