C++;单例设计模式 最近我已经尝试了C++的单体设计模式的实现。它看起来是这样的(我从现实生活的例子中采用了它):
从这个声明中,我可以推断实例字段是在堆上启动的。这意味着存在内存分配。对我来说完全不清楚的是,内存何时会被释放?还是有bug和内存泄漏?似乎在执行方面存在问题C++;单例设计模式 最近我已经尝试了C++的单体设计模式的实现。它看起来是这样的(我从现实生活的例子中采用了它):,c++,design-patterns,singleton,C++,Design Patterns,Singleton,从这个声明中,我可以推断实例字段是在堆上启动的。这意味着存在内存分配。对我来说完全不清楚的是,内存何时会被释放?还是有bug和内存泄漏?似乎在执行方面存在问题 #include <Windows.h> #include <iostream> using namespace std; class SingletonClass { public: static SingletonClass* getInstance() { return (!m_in
#include <Windows.h>
#include <iostream>
using namespace std;
class SingletonClass {
public:
static SingletonClass* getInstance() {
return (!m_instanceSingleton) ?
m_instanceSingleton = new SingletonClass :
m_instanceSingleton;
}
private:
// private constructor and destructor
SingletonClass() { cout << "SingletonClass instance created!\n"; }
~SingletonClass() {}
// private copy constructor and assignment operator
SingletonClass(const SingletonClass&);
SingletonClass& operator=(const SingletonClass&);
static SingletonClass *m_instanceSingleton;
};
SingletonClass* SingletonClass::m_instanceSingleton = nullptr;
int main(int argc, const char * argv[]) {
SingletonClass *singleton;
singleton = singleton->getInstance();
cout << singleton << endl;
// Another object gets the reference of the first object!
SingletonClass *anotherSingleton;
anotherSingleton = anotherSingleton->getInstance();
cout << anotherSingleton << endl;
Sleep(5000);
return 0;
}
我的主要问题是,如何以正确的方式实现它?作为一个单体,您通常不希望它被破坏
当程序终止时,它将被拆掉并释放,这是单例的正常行为。如果您希望能够显式地清理它,那么向类中添加一个静态方法非常容易,该方法允许您将其恢复为干净状态,并在下次使用时重新分配,但这超出了“经典”单例的范围。您可以避免内存分配。有许多变体,在多线程环境中都有问题 我更喜欢这种类型的实现(实际上,我并不正确地说我更喜欢,因为我尽可能避免使用单例):
它没有动态内存分配。它确实可能是从堆中分配的,但是如果没有源,就无法知道 典型的实现(取自我在emacs中已有的一些代码)是:
Singleton * Singleton::getInstance() {
if (!instance) {
instance = new Singleton();
};
return instance;
};
…然后依靠超出范围的程序进行清理
如果您在一个必须手动完成清理的平台上工作,我可能会添加一个手动清理例程
这样做的另一个问题是它不是线程安全的。在多线程环境中,两个线程都可以在任何一个线程有机会分配新实例之前通过“if”(如果)。如果您仍然依赖程序终止来清理,这仍然不是什么大问题。另一个非分配的替代方案:创建一个单例,例如类
C
,根据需要:
singleton<C>()
singleton()
使用
模板
X&singleton()
{
静态X;
返回x;
}
这两个问题在当前C++中都不是自动线程安全的,但将在C++ 0x.
中,我提供了一个C++ 2008的实现,它是一个懒惰的、有保证的销毁的单体设计模式,而不是技术上的线程安全:
请参阅这两篇关于初始化顺序和如何处理的文章:
请参阅这篇描述生命周期的文章:
请参阅这篇讨论单例线程含义的文章:
请参阅这篇解释为什么双重检查锁定在C++上不起作用的文章:
接受答案中的解决方案有一个明显的缺点-在控件离开
main()
函数后调用单例的析构函数。当在main
中分配一些依赖对象时,可能确实存在问题
我在尝试在Qt应用程序中引入单例时遇到了这个问题。我决定,我所有的设置对话框都必须是单例的,并采用了上面的模式。不幸的是,Qt的主类QApplication
是在main
函数的堆栈上分配的,当没有可用的应用程序对象时,Qt禁止创建/销毁对话框
这就是为什么我更喜欢堆分配的单例。我为所有单例提供了显式的
init()
和term()
方法,并在main
内部调用它们。因此,我可以完全控制单例创建/销毁的顺序,并且我保证单例将被创建,无论是否有人调用getInstance()
。这是关于对象生命周期管理的。假设您的软件中有多个单例。他们依靠单身汉。在应用程序销毁期间,假设另一个单例对象使用记录器记录其销毁步骤。您必须保证记录器应该最后清理。因此,请同时查看本文:
上面链接的文章描述了双重检查锁定的缺点,即编译器可能会在调用对象的构造函数之前为对象分配内存并设置指向分配内存地址的指针。然而,C++中很容易使用分配器来手动分配内存,然后使用构造调用初始化内存。使用这种方法,双重检查锁定工作正常。如果要在堆中分配对象,为什么不使用唯一指针呢。内存也将被释放,因为我们使用的是唯一指针
class S
{
public:
static S& getInstance()
{
if( m_s.get() == 0 )
{
m_s.reset( new S() );
}
return *m_s;
}
private:
static std::unique_ptr<S> m_s;
S();
S(S const&); // Don't Implement
void operator=(S const&); // Don't implement
};
std::unique_ptr<S> S::m_s(0);
S类
{
公众:
静态S&getInstance()
{
如果(m_.s.get()==0)
{
m_.s.重置(新的s());
}
返回*m_;
}
私人:
静态std::唯一的ptr m;
S();
S(S const&);//不实现
void运算符=(S常量&);//不实现
};
std::unique_ptr S::m_S(0);
例如:
class CCtrl
{
private:
CCtrl(void);
virtual ~CCtrl(void);
public:
INS(CCtrl);
这里是一个简单的实现
#include <Windows.h>
#include <iostream>
using namespace std;
class SingletonClass {
public:
static SingletonClass* getInstance() {
return (!m_instanceSingleton) ?
m_instanceSingleton = new SingletonClass :
m_instanceSingleton;
}
private:
// private constructor and destructor
SingletonClass() { cout << "SingletonClass instance created!\n"; }
~SingletonClass() {}
// private copy constructor and assignment operator
SingletonClass(const SingletonClass&);
SingletonClass& operator=(const SingletonClass&);
static SingletonClass *m_instanceSingleton;
};
SingletonClass* SingletonClass::m_instanceSingleton = nullptr;
int main(int argc, const char * argv[]) {
SingletonClass *singleton;
singleton = singleton->getInstance();
cout << singleton << endl;
// Another object gets the reference of the first object!
SingletonClass *anotherSingleton;
anotherSingleton = anotherSingleton->getInstance();
cout << anotherSingleton << endl;
Sleep(5000);
return 0;
}
这里00915CB8是singleton对象的内存位置,在程序运行期间相同,但每次程序运行时(通常!)都不同
注意:这不是线程安全的。您必须确保线程安全。除了这里的其他讨论之外,可能值得注意的是,您可以具有全局性,而不限制对一个实例的使用。例如,考虑引用计数某物的情况…
struct Store{
std::array<Something, 1024> data;
size_t get(size_t idx){ /* ... */ }
void incr_ref(size_t idx){ /* ... */}
void decr_ref(size_t idx){ /* ... */}
};
template<Store* store_p>
struct ItemRef{
size_t idx;
auto get(){ return store_p->get(idx); };
ItemRef() { store_p->incr_ref(idx); };
~ItemRef() { store_p->decr_ref(idx); };
};
Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances
裁判堂
#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}
class CCtrl
{
private:
CCtrl(void);
virtual ~CCtrl(void);
public:
INS(CCtrl);
#include <Windows.h>
#include <iostream>
using namespace std;
class SingletonClass {
public:
static SingletonClass* getInstance() {
return (!m_instanceSingleton) ?
m_instanceSingleton = new SingletonClass :
m_instanceSingleton;
}
private:
// private constructor and destructor
SingletonClass() { cout << "SingletonClass instance created!\n"; }
~SingletonClass() {}
// private copy constructor and assignment operator
SingletonClass(const SingletonClass&);
SingletonClass& operator=(const SingletonClass&);
static SingletonClass *m_instanceSingleton;
};
SingletonClass* SingletonClass::m_instanceSingleton = nullptr;
int main(int argc, const char * argv[]) {
SingletonClass *singleton;
singleton = singleton->getInstance();
cout << singleton << endl;
// Another object gets the reference of the first object!
SingletonClass *anotherSingleton;
anotherSingleton = anotherSingleton->getInstance();
cout << anotherSingleton << endl;
Sleep(5000);
return 0;
}
SingletonClass instance created!
00915CB8
00915CB8
struct Store{
std::array<Something, 1024> data;
size_t get(size_t idx){ /* ... */ }
void incr_ref(size_t idx){ /* ... */}
void decr_ref(size_t idx){ /* ... */}
};
template<Store* store_p>
struct ItemRef{
size_t idx;
auto get(){ return store_p->get(idx); };
ItemRef() { store_p->incr_ref(idx); };
~ItemRef() { store_p->decr_ref(idx); };
};
Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances
auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201);
template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning
instances of ItemRef<Store_t, store_p>. */ };
class Singleton
{
public:
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static std::shared_ptr<Singleton> instance()
{
static std::shared_ptr<Singleton> s{new Singleton};
return s;
}
private:
Singleton() {}
};
template<typename HeirT>
class Singleton
{
public:
Singleton() = delete;
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
static HeirT &instance()
{
static HeirT instance;
return instance;
}
};
#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H
class SingletonClass
{
public:
static SingletonClass* Instance()
{
static SingletonClass* instance = new SingletonClass();
return instance;
}
void Relocate(int X, int Y, int Z);
private:
SingletonClass();
~SingletonClass();
};
#define sSingletonClass SingletonClass::Instance()
#endif
sSingletonClass->Relocate(1, 2, 5);
#pragma once
#include <memory>
template<typename T>
class Singleton
{
private:
static std::weak_ptr<T> _singleton;
public:
static std::shared_ptr<T> singleton()
{
std::shared_ptr<T> singleton = _singleton.lock();
if (!singleton)
{
singleton.reset(new T());
_singleton = singleton;
}
return singleton;
}
};
template<typename T>
std::weak_ptr<T> Singleton<T>::_singleton;
class Singleton {
public:
static Singleton* get_instance();
// disable copy/move -- this is a Singleton
Singleton(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton& operator=(Singleton&&) = delete;
friend class Singleton_destroyer;
private:
Singleton(); // no one else can create one
~Singleton(); // prevent accidental deletion
static Singleton* ptr;
};
// auxiliary static object for destroying the memory of Singleton
class Singleton_destroyer {
public:
~Singleton_destroyer { delete Singleton::ptr; }
};
// somewhere in code (Singleton.cpp is probably the best place)
// create a global static Singleton_destroyer object
Singleton_destoyer the_destroyer;
// public member function
static Singleton& Singleton::get_instance()
{
static Singleton s;
return s;
}
class Singleton
{
public:
static Singleton* getInstance( );
private:
Singleton( );
static Singleton* instance;
};
Singleton* Singleton::instance; //we need to declare outside because static variables are global
#include <iostream>
#include <thread>
class Singleton
{
private:
static Singleton * _instance;
static std::mutex mutex_;
protected:
Singleton(const std::string value): value_(value)
{
}
~Singleton() {}
std::string value_;
public:
/**
* Singletons should not be cloneable.
*/
Singleton(Singleton &other) = delete;
/**
* Singletons should not be assignable.
*/
void operator=(const Singleton &) = delete;
//static Singleton *GetInstance(const std::string& value);
static Singleton *GetInstance(const std::string& value)
{
if (_instance == nullptr)
{
std::lock_guard<std::mutex> lock(mutex_);
if (_instance == nullptr)
{
_instance = new Singleton(value);
}
}
return _instance;
}
std::string value() const{
return value_;
}
};
/**
* Static methods should be defined outside the class.
*/
Singleton* Singleton::_instance = nullptr;
std::mutex Singleton::mutex_;
void ThreadFoo(){
std::this_thread::sleep_for(std::chrono::milliseconds(10));
Singleton* singleton = Singleton::GetInstance("FOO");
std::cout << singleton->value() << "\n";
}
void ThreadBar(){
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Singleton* singleton = Singleton::GetInstance("BAR");
std::cout << singleton->value() << "\n";
}
int main()
{
std::cout <<"If you see the same value, then singleton was reused (yay!\n" <<
"If you see different values, then 2 singletons were created (booo!!)\n\n" <<
"RESULT:\n";
std::thread t1(ThreadFoo);
std::thread t2(ThreadBar);
t1.join();
t2.join();
std::cout << "Complete!" << std::endl;
return 0;
}
#include<iostream>
#include<string>
class DoNotCopy
{
protected:
DoNotCopy(void) = default;
DoNotCopy(const DoNotCopy&) = delete;
DoNotCopy& operator=(const DoNotCopy&) = delete;
};
class DoNotMove
{
protected:
DoNotMove(void) = default;
DoNotMove(DoNotMove&&) = delete;
DoNotMove& operator=(DoNotMove&&) = delete;
};
class DoNotCopyMove : public DoNotCopy,
public DoNotMove
{
protected:
DoNotCopyMove(void) = default;
};
template<class T>
class Singleton : public DoNotCopyMove
{
public:
static T& Instance(void)
{
static T instance;
return instance;
}
protected:
Singleton(void) = default;
};
class Logger final: public Singleton<Logger>
{
public:
void log(const std::string& str) { std::cout << str << std::endl; }
};
int main()
{
Logger::Instance().log("xx");
}