但我无法理解为什么在下一个例子中,如果我在变量Singleton
中实例化inst
,它的析构函数会被调用两次?
#include <iostream>
class Singleton
{
public:
~Singleton() { std::cout << "destruction!\n"; }
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
void foo() { std::cout << "foo!\n"; }
private:
Singleton() { std::cout << "construction!\n"; }
};
int main()
{
Singleton inst = Singleton::getInstance();
inst.foo();
}
输出:
construction!
foo!
destruction!
destruction!
更正确的是,我理解为什么它被调用两次。但是我无法理解如何如果在第一个析构函数之后该类的实例被销毁,它可以被调用两次?为什么没有例外?
或未被销毁?为什么呢?
答案 0 :(得分:17)
这一行
Singleton inst = Singleton::getInstance();
应该是
Singleton& inst = Singleton::getInstance();
然后你只会看到一个析构函数调用。
编写方式时,Singleton::getInstance()
会返回引用,但将复制到inst
。因此,从您的函数和返回的Singleton
都会销毁副本。你从未看过复制构造,因为没有使用默认构造函数,复制构造函数是。
在第二种方法中,返回引用,然后您只需inst
作为Singleton
的引用,而不是复制。
正如其他人所提到的那样,你可以使这个类不可复制和不可移动以防止这种情况
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 :(得分:8)
该行
Singleton inst = Singleton::getInstance();
使用自动生成的复制构造函数复制您的实例。为防止这种情况发生,请添加
Singleton( const Singleton& ) = delete;
到您的班级以防止这些复印件。为了确保捕捉到更加模糊的错误,还要添加
void operator=( const Singleton& ) = delete;
也是。您不必显式删除移动构造或赋值,因为编译器不会在声明其他(已删除)成员的情况下生成它们。
答案 2 :(得分:3)
尝试添加公共副本构造函数:
Singleton(const Singleton&) { std::cout << "copy construction!\n"; }
您的输出将变为:
construction!
copy construction!
foo!
destruction!
destruction!
有两个&#34;单身&#34;因为来自getInstance()
的那个被复制而活着。为避免无意中复制单例,您应该删除复制构造函数和赋值运算符:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
答案 3 :(得分:2)
您可以将单例隐藏在不具有静态性质的常规类中:
#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() << '\n';
}