我对单身人士模式有疑问.
我在单例类中看到了两个关于静态成员的案例.
首先它是一个像这样的对象
class CMySingleton
{
public:
static CMySingleton& Instance()
{
static CMySingleton singleton;
return singleton;
}
// Other non-static member functions
private:
CMySingleton() {} // Private constructor
~CMySingleton() {}
CMySingleton(const CMySingleton&); // Prevent copy-construction
CMySingleton& operator=(const CMySingleton&); // Prevent assignment
};
Run Code Online (Sandbox Code Playgroud)
一个是指针,就像这样
class GlobalClass
{
int m_value;
static GlobalClass *s_instance;
GlobalClass(int v = 0)
{
m_value = v;
}
public:
int get_value()
{
return m_value;
}
void set_value(int v)
{
m_value = v;
}
static GlobalClass *instance()
{
if (!s_instance)
s_instance = new GlobalClass;
return s_instance;
}
};
Run Code Online (Sandbox Code Playgroud)
这两种情况有什么区别?哪一个是正确的?
Mat*_* M. 60
你应该阅读Alexandrescu的书.
关于本地静态,我暂时没有使用Visual Studio,但是在使用Visual Studio 2003编译时,每个DLL分配了一个本地静态...谈论调试的噩梦,我会记得一个用于同时:/
1.单身人士的一生
关于单身人士的主要问题是终身管理.
如果你试图使用这个物体,你需要活着并且踢.因此问题来自初始化和破坏,这是C++中使用全局变量的常见问题.
初始化通常是最容易纠正的事情.正如两种方法所暗示的那样,在初次使用时初始化非常简单.
破坏更加微妙.全局变量按照创建它们的相反顺序销毁.所以在本地静态情况下,你实际上并不控制事物....
2.局部静电
struct A
{
A() { B::Instance(); C::Instance().call(); }
};
struct B
{
~B() { C::Instance().call(); }
static B& Instance() { static B MI; return MI; }
};
struct C
{
static C& Instance() { static C MI; return MI; }
void call() {}
};
A globalA;
Run Code Online (Sandbox Code Playgroud)
这有什么问题?让我们检查一下构造函数和析构函数的调用顺序.
一,施工阶段:
A globalA;被执行,A::A()被称为A::A() 电话 B::B()A::A() 电话 C::C()它运行正常,因为我们在第一次访问时初始化B和C实例.
二,破坏阶段:
C::~C() 被称为因为它是3的最后一个构造B::~B()被称为... oups,它试图访问C的实例!因此,我们在破坏时有不明确的行为,哼......
3.新战略
这里的想法很简单.全局内置函数在其他全局变量之前初始化,因此您的指针将被设置为0在您编写的任何代码被调用之前,它确保测试:
S& S::Instance() { if (MInstance == 0) MInstance = new S(); return *MInstance; }
Run Code Online (Sandbox Code Playgroud)
实际上会检查实例是否正确.
不过已经说过,这里有一个内存泄漏,最糟糕的是一个永远不会被调用的析构函数.解决方案存在,并且是标准化的.这是对atexit函数的调用.
该atexit函数允许您指定在程序关闭期间执行的操作.有了这个,我们可以写一个单身就好了:
// in s.hpp
class S
{
public:
static S& Instance(); // already defined
private:
static void CleanUp();
S(); // later, because that's where the work takes place
~S() { /* anything ? */ }
// not copyable
S(S const&);
S& operator=(S const&);
static S* MInstance;
};
// in s.cpp
S* S::MInstance = 0;
S::S() { atexit(&CleanUp); }
S::CleanUp() { delete MInstance; MInstance = 0; } // Note the = 0 bit!!!
Run Code Online (Sandbox Code Playgroud)
首先,让我们了解更多atexit.签名是int atexit(void (*function)(void));,即它接受一个指向函数的指针,该函数不带任何参数,也不返回任何内容.
第二,它是如何工作的?好吧,与前一个用例完全一样:在初始化时,它会构建一个指向函数的指针堆栈,以便在调用时将堆栈清空一次.因此,实际上,函数以Last-In First-Out方式调用.
那么这里发生了什么?
第一次访问时构造(初始化很好),我注册CleanUp退出时间的方法
退出时间:CleanUp调用该方法.它会破坏对象(因此我们可以在析构函数中有效地工作)并重置指针以0发出信号.
如果(例如with A,B和C)我调用已经被破坏的对象的实例会发生什么?好吧,在这种情况下,因为我将指针设置为0我将重建一个临时单例并且循环重新开始.它不会活很长时间,因为我正在我的堆栈.
亚历山大雷斯库(Alexandrescu)称它为Phoenix Singleton灰烬,如果它被摧毁后需要它从灰烬中复活.
另一种方法是使用静态标志并destroyed在清理期间将其设置为并让用户知道它没有获得单例的实例,例如通过返回空指针.返回指针(或引用)的唯一问题是你最好希望没有人愚蠢到可以调用delete它:/
4.幺半群模式
既然我们在谈论Singleton我认为是时候介绍Monoid模式了.从本质上讲,它可以被视为Flyweight模式的退化情况,或者使用Proxy结束Singleton.
该Monoid模式很简单:类共享公共状态的所有实例.
我将借此机会揭露非凤凰实施:)
class Monoid
{
public:
void foo() { if (State* i = Instance()) i->foo(); }
void bar() { if (State* i = Instance()) i->bar(); }
private:
struct State {};
static State* Instance();
static void CleanUp();
static bool MDestroyed;
static State* MInstance;
};
// .cpp
bool Monoid::MDestroyed = false;
State* Monoid::MInstance = 0;
State* Monoid::Instance()
{
if (!MDestroyed && !MInstance)
{
MInstance = new State();
atexit(&CleanUp);
}
return MInstance;
}
void Monoid::CleanUp()
{
delete MInstance;
MInstance = 0;
MDestroyed = true;
}
Run Code Online (Sandbox Code Playgroud)
有什么好处?它隐藏了这样一个事实:国家是共享的,它隐藏着Singleton.
Singleton通过调用替换Factory)delete给你的单身人士的实例,所以你真正管理状态并防止意外......无论如何你无法对恶意用户做多少事情!5.最后一句话
尽管看起来很完整,但我想指出我很高兴地浏览了任何多线程问题...阅读Alexandrescu的Modern C++以了解更多信息!