我对单身人士模式有疑问。
我在单例类中看到了两个关于静态成员的案例。
首先它是一个对象,就像这样
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
};
一个是指针,就像这个
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;
}
};
这两种情况有什么区别?哪一个是正确的?
答案 0 :(得分:60)
答案 1 :(得分:4)
两者都不比另一个更正确。我倾向于试图避免一般使用Singleton,但是当我面对认为它是要走的路时,我已经使用了这两个并且它们工作正常。
指针选项的一个故障是它会泄漏内存。另一方面,你的第一个例子可能会在你完成它之前被摧毁,所以如果你不选择为这个东西找出一个更合适的所有者,你就会有一场工资战,在适当的时间创造并销毁它。
答案 2 :(得分:2)
不同之处在于第二个泄漏内存(单身本身)而第一个没有。静态对象在第一次调用它们的关联方法时被初始化,并且(只要程序干净地退出)它们在程序退出之前被销毁。带指针的版本将在程序出口处分配指针,而像Valgrind这样的内存检查器会抱怨。
另外,是什么阻止某人做delete GlobalClass::instance();
?
由于以上两个原因,使用静态的版本是更常用的方法,也是原始设计模式书中规定的方法。
答案 3 :(得分:1)
使用第二种方法 - 如果你不想使用atexit释放你的对象,那么你总是可以使用keeper对象(例如auto_ptr,或者自写的东西)。在完成对象之前,这可能会导致释放,就像第一个方法一样。
不同之处在于,如果你使用静态对象,你基本上无法检查它是否已经被释放。
如果你使用指针,你可以添加额外的静态bool来指示单例是否已被销毁(如Monoid)。然后你的代码总是可以检查单例是否已被销毁,虽然你可能会失败,但至少你不会得到神秘的“段落故障”或“访问冲突”,程序将避免异常终止。 / p>
答案 4 :(得分:1)
我同意比利的观点。在第二种方法中,我们使用 new 从堆中动态分配内存。除非调用删除,否则此内存始终保持且永远不会被释放。因此,全局指针方法会造成内存泄漏。
class singleton
{
private:
static singleton* single;
singleton()
{ }
singleton(const singleton& obj)
{ }
public:
static singleton* getInstance();
~singleton()
{
if(single != NULL)
{
single = NULL;
}
}
};
singleton* singleton :: single=NULL;
singleton* singleton :: getInstance()
{
if(single == NULL)
{
single = new singleton;
}
return single;
}
int main() {
singleton *ptrobj = singleton::getInstance();
delete ptrobj;
singleton::getInstance();
delete singleton::getInstance();
return 0;
}
答案 5 :(得分:0)
你的第一个例子对于单身人士更为典型。您的第二个示例有所不同,因为它是按需创建的。
但是我会尽量避免使用单例,因为它们只不过是全局变量。
答案 6 :(得分:0)
更好的方法是创建单例类。这也避免了GetInstance()函数中的实例可用性检查。这可以使用函数指针来实现。
class TSingleton;
typedef TSingleton* (*FuncPtr) (void);
class TSingleton {
TSingleton(); //prevent public object creation
TSingleton (const TSingleton& pObject); // prevent copying object
static TSingleton* vObject; // single object of a class
static TSingleton* CreateInstance (void);
static TSingleton* Instance (void);
public:
static FuncPtr GetInstance;
};
FuncPtr TSingleton::GetInstance = CreateInstance;
TSingleton* TSingleton::vObject;
TSingleton::TSingleton()
{
}
TSingleton::TSingleton(const TSingleton& pObject)
{
}
TSingleton* TSingleton::CreateInstance(void)
{
if(vObject == NULL){
// Introduce here some code for taking lock for thread safe creation
//...
//...
//...
if(vObject == NULL){
vObject = new TSingleton();
GetInstance = Instance;
}
}
return vObject;
}
TSingleton* TSingleton::Instance(void)
{
return vObject;
}
void main()
{
TSingleton::GetInstance(); // this will call TSingleton::Createinstance()
TSingleton::GetInstance(); // this will call TSingleton::Instance()
// all further calls to TSingleton::GetInstance will call TSingleton::Instance() which simply returns already created object.
}
答案 7 :(得分:-1)
针对“内存泄漏”投诉,有一个简单的解决方法:
// dtor
~GlobalClass()
{
if (this == s_instance)
s_instance = NULL;
}
换句话说,给类提供一个析构函数,当在程序终止时销毁单例对象时,它会解除隐藏指针变量的初始化。
完成此操作后,这两种形式几乎完全相同。唯一重要的区别是,一个返回对隐藏对象的引用,而另一个返回指向它的指针。
<强>更新强>
正如@BillyONeal指出的那样,这不起作用,因为指向对象永远不会被删除。哎哟。
我讨厌考虑它,但你可以使用atexit()
来做肮脏的工作。啧。
哦,好吧,没关系。