C++ 使用std::unique\u ptr的带私有析构函数的单例
我在我的程序中创建了所有单例,并记住了该文档: (如果有人想知道为什么是singleton,那么它们都是工厂,其中一些存储了一些关于如何创建实例的全局设置) 他们每个人看起来都像这样:C++ 使用std::unique\u ptr的带私有析构函数的单例,c++,c++11,singleton,destructor,unique-ptr,C++,C++11,Singleton,Destructor,Unique Ptr,我在我的程序中创建了所有单例,并记住了该文档: (如果有人想知道为什么是singleton,那么它们都是工厂,其中一些存储了一些关于如何创建实例的全局设置) 他们每个人看起来都像这样: class SingletonFactory { static std::unique_ptr<SingletonFactory, void (*)(SingletonFactory*)> volatile instance; public: static SingletonFact
class SingletonFactory {
static std::unique_ptr<SingletonFactory, void (*)(SingletonFactory*)> volatile instance;
public:
static SingletonFactory& getInstance();
private:
SingletonFactory();
SingletonFactory(
const SingletonFactory& ingletonFactory
);
~SingletonFactory();
void deleter(SingletonFactory *d) { d->~SingletonFactory(); free(d); }
};
声明:
class SingletonAndFactory {
static SingletonAndFactory* volatile instance;
public:
static SingletonAndFactory& getInstance();
private:
SingletonAndFactory();
SingletonAndFactory(
const SingletonAndFactory& ingletonFactory
);
~SingletonAndFactory();
};
定义:
boost::mutex singletonAndFactoryMutex;
////////////////////////////////////////////////////////////////////////////////
// class SingletonAndFactory {
SingletonAndFactory* volatile singletonAndFactory::instance = 0;
// public:
SingletonAndFactory& SingletonAndFactory::getInstance() {
// Singleton implemented according to:
// "C++ and the Perils of Double-Checked Locking".
if (!instance) {
boost::mutex::scoped_lock lock(SingletonAndFactoryMutex);
if (!instance) {
SingletonAndFactory* volatile tmp = (SingletonAndFactory*) malloc(sizeof(SingletonAndFactory));
new (tmp) SingletonAndFactory; // placement new
instance = tmp;
}
}
return *instance;
}
// private:
SingletonAndFactory::SingletonAndFactory() {}
// };
撇开问题不谈singleton的什么设计是最好的(因为它会引发一场毫无意义的火焰战争),我的问题是:用std::unique\u ptr替换普通指针对我有好处吗?特别是,它会在程序退出时调用singleton的析构函数吗?如果是,我将如何实现它?当我尝试添加类似于friend class std::unique\u ptr代码>由于编译器不断抱怨析构函数是私有的,所以它并没有成功
我知道这在我当前的项目中并不重要,因为没有一家工厂有任何需要清洁的东西,但为了将来的参考,我想知道如何实现这种行为。不是唯一的\u ptr
本身进行删除,而是删除器。因此,如果您想使用friend
方法,您必须这样做:
friend std::unique_ptr<SingletonFactory>::deleter_type;
在创建函数中:
SingletonFactory* volatile tmp = (SingletonFactory*) malloc(sizeof(SingletonFactory));
new (tmp) SingletonFactory; // placement new
instance = decltype(instance)(tmp, &deleter);
在C++11中,您可以在程序结束时使用本地静态文件保证线程安全的惰性初始化和销毁:
SingletonAndFactory& SingletonAndFactory::getInstance() {
static SingletonAndFactory instance;
return instance;
}
请注意,这仍然会导致生存期问题,因为它可能会在其他静态对象之前被销毁。如果他们试图从他们的析构函数中访问它,那么你就有麻烦了
在此之前,这是不可能的(尽管许多编译器都保证了这一点)。正如您链接到的文档中所述,volatile
与线程同步无关,因此您的代码存在数据竞争和未定义的行为。选项包括:
- 利用锁定的性能(可能很大)来检查指针
- 使用编译器提供的任何不可移植的原子内部函数来测试指针
- 忘记线程安全初始化,并确保在启动线程之前对其进行初始化
- 不要使用单例
我赞成最后一个选项,因为它解决了Singleton反模式引入的所有其他问题。Thing&getting(){static Thing Thing;return Thing;}
忘记锁,忘记Singleton。还有。。。真的吗<代码>malloc
+新位置?噢,得了吧。…@R.MartinhoFernandes:如果你要使用单例,你最好尽量把它们弄错。我也怀疑指针是否用于内存映射I/O,因为它被标记为volatile
。C++11没有为volatile
分配与线程相关的语义。看起来volatile
是从链接文档中的一个示例复制的,该示例描述了volatile
如何不足以保证线程安全。您需要C++11来提供必要的原子性保证;一旦你有了C++11,你也可以使用一个本地静态的。谢谢!这就是我一直在寻找的答案。我知道有几种方法可以实现单身行为(例如),其中一些方法的区别只是味道。我在寻找一种方法来改进我自己的实现。getInstance()的胆量可以很容易地切换到其他东西,我可能很快就会这样做(因为我切换到C++11),但我想从整体上改进我的设计。@令人恼火的是,我没有把它放在答案中(因为我没有时间阅读你问题中的链接),但请密切注意其他人对你的问题的评论。代码中的许多设计决策(placementnew
,volatile
)充其量都是有问题的。我很清楚这一点。我目前正在研究如何在我的项目中改进代码。实际上,这是一个自我学习项目,我可以通过实验来更好地理解C++。我当然会使用其中的一些建议,不过——就目前而言——单身人士将留下来。