Mi informacion de contacto
Correo[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
En el desarrollo de software, los patrones de diseño son soluciones comprobadas a problemas específicos. Ayudan a los desarrolladores a crear software de una manera más consistente, reutilizable y mantenible. El patrón Singleton es uno de los patrones de diseño más básicos y más utilizados. Garantiza que una clase tenga una sola instancia y proporciona un punto de acceso global para obtener esta instancia.Este artículo analizará en profundidad cómo implementar el patrón Singleton en C++, desde la implementación básica hasta versiones seguras para subprocesos y características modernas de C++, como punteros inteligentes ystd::call_once
) aplicación y nos esforzamos por proporcionar a los lectores una guía completa y práctica.
La idea central del patrón singleton es garantizar que una clase tenga solo una instancia y proporcionar un punto de acceso global. Este modo es útil cuando necesita controlar el acceso a recursos (como lectores de archivos de configuración, registradores, grupos de conexiones de bases de datos, etc.). La clave del patrón singleton es:
getInstance()
, utilizado para obtener la única instancia de una clase.Primero, veamos una implementación simple de un patrón singleton sin considerar los problemas de seguridad de los subprocesos:
#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;
}
En un entorno de subprocesos múltiples, la implementación anterior puede hacer que entren varios subprocesos al mismo tiempo.getInstance()
método y crear instancias varias veces.Para resolver este problema, podemos usar un bloqueo mutex (comostd::mutex
) para garantizar la seguridad del hilo:
#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函数保持不变
Para gestionar automáticamente el ciclo de vida de un objeto singleton (es decir, destruido automáticamente al final del programa), podemos utilizar punteros inteligentes (comostd::unique_ptr
) en lugar de punteros sin formato:
#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
mejoramientoA partir de C++11,std::call_once
Proporciona una forma más eficiente y concisa de garantizar que una función se llame solo una vez, incluso en un entorno de subprocesos múltiples. Podemos utilizar esta característica para optimizar aún más la implementación del patrón 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: En el código anterior, cité incorrectamente#include <once.h>
, uno debería realmente usar<mutex>
en el archivo de encabezadostd::once_flag
ystd::call_once
。
El patrón singleton es un patrón de diseño muy útil en C++. Garantiza que una clase tenga una sola instancia y proporciona un punto de acceso global. Sin embargo, debe prestar atención a los problemas de seguridad de los subprocesos al implementar el patrón singleton, especialmente en un entorno de subprocesos múltiples.Mediante el uso de bloqueos mutex, punteros inteligentes ystd::call_once
Con las características modernas de C++, podemos implementar el patrón singleton de manera más segura y eficiente.
Al diseñar el patrón singleton, también debe considerar algunos factores adicionales, como la gestión del ciclo de vida del objeto singleton (si debe destruirse automáticamente cuando finalice el programa), si se permite la carga diferida (es decir, retrasar el creación de una instancia hasta su primer uso), etc. Además, en algunos casos, es posible que desee considerar variaciones del patrón singleton, como el patrón de instancias múltiples (que controla el número de instancias de clase, pero no excede un cierto límite superior) o el patrón singleton basado en contexto ( que devuelve diferentes valores según diferentes contextos (instancia).
Espero que este artículo pueda ayudar a los lectores a comprender mejor el patrón singleton en C++ y utilizarlo de manera flexible en proyectos reales.