le mie informazioni di contatto
Posta[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Nello sviluppo del software, i modelli di progettazione sono soluzioni comprovate a problemi specifici che aiutano gli sviluppatori a creare software in modo più coerente, riutilizzabile e gestibile. Il modello Singleton è uno dei modelli di progettazione più basilari e ampiamente utilizzati. Assicura che una classe abbia una sola istanza e fornisce un punto di accesso globale per ottenere questa istanza.Questo articolo esaminerà in modo approfondito come implementare il pattern Singleton in C++, dall'implementazione di base alle versioni thread-safe alle moderne funzionalità C++ come puntatori intelligenti estd::call_once
) applicazione e si sforzano di fornire ai lettori una guida completa e pratica.
L'idea centrale del modello singleton è garantire che una classe abbia una sola istanza e fornire un punto di accesso globale. Questa modalità è utile quando è necessario controllare l'accesso alle risorse (come lettori di file di configurazione, logger, pool di connessioni al database, ecc.). La chiave del modello singleton è:
getInstance()
, utilizzato per ottenere l'unica istanza di una classe.Innanzitutto, diamo un'occhiata a una semplice implementazione del modello singleton senza considerare i problemi di sicurezza del thread:
#include <iostream>
class Singleton {
private:
// 私有构造函数,防止外部实例化
Singleton() {}
// 私有拷贝构造函数和赋值操作符,防止拷贝
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 静态实例
static Singleton* instance;
public:
// 静态方法,返回类的唯一实例
static Singleton* getInstance() {
if (!instance) {
instance = new Singleton();
}
return instance;
}
// 示例方法
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
// 析构函数(通常是protected或public,取决于是否需要外部delete)
~Singleton() {
std::cout << "Singleton destroyed." << std::endl;
}
};
// 初始化静态实例
Singleton* Singleton::instance = nullptr;
int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
if (s1 == s2) {
std::cout << "s1 and s2 are the same instance." << std::endl;
}
s1->doSomething();
// 注意:在多线程环境下,上述实现可能存在安全问题
// 通常不推荐手动删除单例对象,除非有特别理由
// delete Singleton::instance; // 谨慎使用
return 0;
}
In un ambiente multi-thread, l'implementazione di cui sopra potrebbe causare l'ingresso di più thread contemporaneamentegetInstance()
metodo e creare istanze più volte.Per risolvere questo problema, possiamo usare un blocco mutex (comestd::mutex
) per garantire la sicurezza del thread:
#include <mutex>
#include <iostream>
class Singleton {
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* instance;
static std::mutex mtx;
public:
static Singleton* getInstance() {
std::lock_guard<std::mutex> lock(mtx);
if (!instance) {
instance = new Singleton();
}
return instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
~Singleton() {
std::cout << "Singleton destroyed." << std::endl;
}
};
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
// main函数保持不变
Per gestire automaticamente il ciclo di vita di un oggetto singleton (cioè distrutto automaticamente alla fine del programma), possiamo utilizzare puntatori intelligenti (comestd::unique_ptr
) invece di puntatori grezzi:
#include <memory>
#include <mutex>
#include <iostream>
class Singleton {
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static std::unique_ptr<Singleton> instance;
static std::mutex mtx;
public:
static Singleton& getInstance() {
std::lock_guard<std::mutex> lock(mtx);
if (!instance) {
instance = std::make_unique<Singleton>();
}
return *instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
// 析构函数被智能指针管理,无需手动调用
~Singleton() {
std::cout << "Singleton destroyed." << std::endl;
}
// 禁止拷贝和移动
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
};
std::unique_ptr<Singleton> Singleton::instance = nullptr;
std::mutex Singleton::mtx;
int main() {
// 注意这里返回的是引用,无需使用指针
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
if (&s1 == &s2) {
std::cout << "s1 and s2 are the same instance." << std::endl;
}
s1.doSomething();
// 程序结束时,智能指针会自动销毁Singleton实例
return 0;
}
std::call_once
ottimizzazioneA partire da C++11,std::call_once
Fornisce un modo più efficiente e conciso per garantire che una funzione venga chiamata una sola volta, anche in un ambiente multi-thread. Possiamo utilizzare questa funzionalità per ottimizzare ulteriormente l'implementazione del pattern singleton:
#include <memory>
#include <mutex>
#include <iostream>
#include <once.h> // 注意:实际上应使用#include <mutex>中的std::call_once
class Singleton {
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static std::unique_ptr<Singleton> instance;
static std::once_flag onceFlag;
static void createInstance() {
instance = std::make_unique<Singleton>();
}
public:
static Singleton& getInstance() {
std::call_once(onceFlag, createInstance);
return *instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
// 析构函数和移动操作符的禁用同上
};
std::unique_ptr<Singleton> Singleton::instance = nullptr;
std::once_flag Singleton::onceFlag;
// main函数保持不变
NOTA: nel codice sopra ho citato erroneamente#include <once.h>
, si dovrebbe effettivamente usare<mutex>
nel file di intestazionestd::once_flag
Estd::call_once
。
Il modello singleton è un modello di progettazione molto utile in C++. Assicura che una classe abbia una sola istanza e fornisce un punto di accesso globale. Tuttavia, è necessario prestare attenzione ai problemi di sicurezza dei thread quando si implementa il modello singleton, soprattutto in un ambiente multi-thread.Utilizzando blocchi mutex, puntatori intelligenti estd::call_once
Con le moderne funzionalità C++, possiamo implementare il modello singleton in modo più sicuro ed efficiente.
Quando si progetta il pattern singleton, è necessario considerare anche alcuni fattori aggiuntivi, come la gestione del ciclo di vita dell'oggetto singleton (se deve essere distrutto automaticamente al termine del programma), se è consentito il lazy load (ovvero ritardare il creazione di un'istanza fino al primo utilizzo), ecc. Inoltre, in alcuni casi, potresti voler prendere in considerazione variazioni del modello singleton, come il modello multi-istanza (che controlla il numero di istanze della classe, ma non supera un determinato limite superiore) o il modello singleton basato sul contesto ( che restituisce valori diversi in base ai diversi contesti).
Spero che questo articolo possa aiutare i lettori a comprendere meglio il modello singleton in C++ e a utilizzarlo in modo flessibile nei progetti reali.