Singleton: how can destructor be called twice?
几分钟前,我问了一个有关单例实现的问题,@ LightnessRacesinOrbit给出了很好的答案。
但是我不明白为什么在下一个示例中,如果我在变量
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(); |
然后您将只看到一个析构函数调用。
编写方式,
在第二种方法中,返回引用,那么您只需将
就像其他人提到的那样,您可以使该类不可复制且不可移动,以防止这种情况
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 "处于活动状态,因为
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() << '\ '; } |