关于C#:Singleton:析构函数如何被调用两次?

Singleton: how can destructor be called twice?

几分钟前,我问了一个有关单例实现的问题,@ LightnessRacesinOrbit给出了很好的答案。

但是我不明白为什么在下一个示例中,如果我在变量inst中实例化Singleton的析构函数被调用了两次?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

class Singleton
{
public:
    ~Singleton()  { std::cout <<"destruction!\
"
; }

    static Singleton& getInstance()
    {
        static Singleton instance;
        return instance;
    }

    void foo() { std::cout <<"foo!\
"
; }

private:
    Singleton() { std::cout <<"construction!\
"
; }
};

int main()
{
    Singleton inst = Singleton::getInstance();
    inst.foo();
}

输出:

1
2
3
4
construction!
foo!
destruction!
destruction!

现场演示

更正确地说,我理解为什么它被两次调用。但是我不明白,如果在第一个析构函数之后该类的实例被销毁了,怎么可以两次调用它呢?为什么没有例外?

还是没有被销毁?为什么?


此行

1
Singleton inst = Singleton::getInstance();

应该是

1
Singleton& inst = Singleton::getInstance();

然后您将只看到一个析构函数调用。

编写方式,Singleton::getInstance()返回引用,然后将其复制到inst。因此,从函数返回的Singleton和副本均被销毁。您从未见过构造副本的原因,因为未使用默认的构造函数,而副本构造函数是。

在第二种方法中,返回引用,那么您只需将inst作为对该Singleton的引用,而不是进行复制。

就像其他人提到的那样,您可以使该类不可复制且不可移动,以防止这种情况

1
2
3
4
Singleton(Singleton const&) = delete;             // Copy construct
Singleton(Singleton&&) = delete;                  // Move construct
Singleton& operator=(Singleton const&) = delete;  // Copy assign
Singleton& operator=(Singleton &&) = delete;      // Move assign


线

1
Singleton inst = Singleton::getInstance();

使用自动生成的复制构造函数复制您的实例。为防止这种情况发生,请添加

1
Singleton( const Singleton& ) = delete;

到您的班级,以防止意外复制。为确保捕获到更多难以理解的错误,还请添加

1
void operator=( const Singleton& ) = delete;

也是。您不必显式删除移动构造或赋值,因为编译器不会在声明了其他(已删除)成员的情况下生成它们。


尝试添加公共副本构造函数:

1
2
Singleton(const Singleton&) { std::cout <<"copy construction!\
"
; }

您的输出将变为:

1
2
3
4
5
construction!
copy construction!
foo!
destruction!
destruction!

有两个" singletons "处于活动状态,因为getInstance()中的一个被复制了。为避免无意中复制单例,应删除复制构造函数和赋值运算符:

1
2
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;


您可以将单身人士隐藏在不表现其静态性质的常规类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <string>

class Singleton // A bad name for a specific class, you should not generalize
{
    private:
    struct Context {
        std::string name;
        Context()
        :   name("Hello")
        {}
    };

    private:
    static Context& context() { static Context result; return result; }

    public:
    const std::string& name() const { return context().name; }
};

int main()
{
    Singleton a;
    std::cout << a.name() << '\
'
;
}