我按照以下方式编写单例c ++:
class A {
private:
static A* m_pA;
A();
virtual ~A();
public:
static A* GetInstance();
static void FreeInstance();
void WORK1();
void WORK2();
void WORK3();
}
}
A* A::GetInstance() {
if (m_pA == NULL)
m_pA = new A();
return m_pA;
}
A::~A() {
FreeInstance() // Can I write this? are there any potential error?
}
void A::FreeInstance() {
delete m_pA;
m_pA = NULL;
}
谢谢! Evan Teran和sep61.myopenid.com的回答是正确的,非常好! 我的方式是错的,我希望任何人写这样的代码都可以避免我的愚蠢错误。
我的项目中的单例A有一个智能指针向量,另一个线程也可以编辑这个向量,所以当应用程序关闭时,即使我添加了很多CMutex,它总是变得不稳定。多线程错误+单例错误浪费了我一天。
// ------------------------------------------------ ----------- 如果您认为以下示例中存在任何问题,欢迎您进行编辑:
class A {
private:
static A* m_pA;
explicit A();
void A(const A& a);
void A(A &a);
const A& operator=(const A& a);
virtual ~A();
public:
static A* GetInstance();
static void FreeInstance();
void WORK1();
void WORK2();
void WORK3();
}
}
A* A::GetInstance() {
if (m_pA == NULL){
static A self;
m_pA = &self;
}
return m_pA;
}
A::~A() {
}
答案 0 :(得分:193)
为什么每个人都希望将单例作为指针返回? 返回它作为参考似乎更合乎逻辑!
你永远不能手动释放单身人士。你怎么知道谁在提到单身人士?如果您不知道(或不能保证)没有人有引用(在您的情况下通过指针),那么您就没有业务可以释放该对象。
在函数方法中使用static 这保证了它只被创建和销毁一次。它还免费为您提供延迟初始化。
class S
{
public:
static S& getInstance()
{
static S instance;
return instance;
}
private:
S() {}
S(S const&); // Don't Implement.
void operator=(S const&); // Don't implement
};
请注意,您还需要将构造函数设为私有。 还要确保覆盖默认的复制构造函数和赋值运算符,这样就不能复制单例(否则它不会是单例)。
同时阅读:
确保您出于正确的原因使用单身人士。
虽然在一般情况下技术上不是线程安全的,但请参阅:
What is the lifetime of a static variable in a C++ function?
海湾合作委员会有一个明确的补丁来弥补这一点:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00265.html
答案 1 :(得分:12)
您可以通过使用如下静态对象来避免删除它:
if(m_pA == 0) {
static A static_instance;
m_pA = &static_instance;
}
答案 2 :(得分:4)
C ++中的单例可以用这种方式编写:
static A* A::GetInstance() {
static A sin;
return &sin;
}
答案 3 :(得分:2)
不要忘记将复制构造函数和赋值运算符设为私有。
答案 4 :(得分:1)
我认为没有任何理由不写这句话。您的析构函数方法不是静态的,并且您的单例实例不会以这种方式被破坏。我不认为析构函数是必要的,如果你需要使用你已经创建的静态方法清理对象,FreeInstance()。
除此之外,你创造单身的方式与我创造单身的方式大致相同。
答案 5 :(得分:1)
经过一段时间对Meyers风格的单身人士充满热情(使用当地静态对象,如前面的一些答案),我完全厌倦了复杂应用中的终生管理问题。
我倾向于发现你最终会在应用程序的初始化过程中故意引用“实例”方法,以确保它们是在你想要的时候创建的,然后通过拆卸来播放所有类型的游戏事情被破坏的不可预测的(或至少非常复杂的,有些隐藏的)秩序。
YMMV当然,它取决于单身人士本身的性质,但很多关于聪明单身人士(以及围绕聪明的线程/锁定问题)的胡扯被高估了IMO。
答案 6 :(得分:1)
如果您阅读“现代C ++设计”,您会发现单例设计可能比返回静态变量复杂得多。
答案 7 :(得分:0)
基于模式,有一个很棒的C ++库ACE。有很多关于不同类型模式的文档,所以看看他们的工作: http://www.cs.wustl.edu/~schmidt/ACE.html
答案 8 :(得分:0)
只要您能回答以下问题,此实施就可以了:
你知道什么时候会创建对象(如果你使用的是静态对象而不是new?你有main()吗?)
你是否有单身人士有任何依赖,可能在创建时尚未准备好?如果您使用静态对象而不是新对象,那么此时已初始化了哪些库?您的对象在构造函数中可能需要它们的作用是什么?
何时删除?
使用new()更安全,因为您可以控制创建和删除对象的位置和时间。但是你需要明确地删除它,系统中的任何人都不知道何时这样做。如果有意义,你可以使用atexit()。
在方法中使用静态对象意味着确实不知道何时创建或删除它。您也可以在命名空间中使用全局静态对象并完全避免使用getInstance() - 它不会增加太多。
如果您使用线程,那么您就遇到了大麻烦。由于以下原因,在C ++中创建可用的线程安全单例实际上是不可能的:
这些可以轻松搜索,但这里是弱内存模型的一个很好的链接:http://ridiculousfish.com/blog/archives/2007/02/17/barrier。
一种解决方案是使用锁定,但要求用户缓存从getInctance()获取的指针,并为getInstance()做好准备。
另一种解决方案是让用户自己处理线程安全。
另一种解决方案是使用具有简单锁定的函数,并将其替换为另一个函数,而无需在调用new()后锁定和检查。这有效,但完全实现很复杂。
答案 9 :(得分:0)
//! @file singleton.h
//!
//! @brief Variadic template to make a singleton out of an ordinary type.
//!
//! This template makes a singleton out of a type without a default
//! constructor.
#ifndef SINGLETON_H
#define SINGLETON_H
#include <stdexcept>
template <typename C, typename ...Args>
class singleton
{
private:
singleton() = default;
static C* m_instance;
public:
singleton(const singleton&) = delete;
singleton& operator=(const singleton&) = delete;
singleton(singleton&&) = delete;
singleton& operator=(singleton&&) = delete;
~singleton()
{
delete m_instance;
m_instance = nullptr;
}
static C& create(Args...args)
{
if (m_instance != nullptr)
{
delete m_instance;
m_instance = nullptr;
}
m_instance = new C(args...);
return *m_instance;
}
static C& instance()
{
if (m_instance == nullptr)
throw std::logic_error(
"singleton<>::create(...) must precede singleton<>::instance()");
return *m_instance;
}
};
template <typename C, typename ...Args>
C* singleton<C, Args...>::m_instance = nullptr;
#endif // SINGLETON_H