C++ Avanzado: diseño de clase especial

Tabla de contenido

1. Diseña una clase que no se pueda copiar

2. Diseñe una clase que solo pueda crear objetos en el montón

 3. Diseña una clase que solo pueda crear objetos en la pila

4. Diseña una clase que no se pueda heredar

5. Diseñe una clase que solo pueda crear un objeto (modo singleton)

  patrón único

   modo hombre hambriento

   modo perezoso


 

1. Diseña una clase que no se pueda copiar

La copia solo aparecerá en dos escenarios: sobrecarga del constructor de copia y del operador de asignación, por lo que si desea hacer que una clase prohíba la copia, solo necesita hacer que la clase no pueda llamar a la sobrecarga del constructor de copia y del operador de asignación.

En C++98, solo se declaran, no se implementan, la construcción de copias y la sobrecarga de asignaciones, y estas dos funciones miembro se pueden establecer como privadas. Después de establecer privado, no se puede llamar fuera de la clase.Con la declaración, el compilador no lo generará por defecto.

class CopyBan
{
    // ...
    private:
    CopyBan(const CopyBan&);
    CopyBan& operator=(const CopyBan&);
    //...
};

El uso de delete se amplía en C++ 11. Además de liberar los recursos solicitados por new, si delete sigue a la función miembro predeterminada con =delete, significa que el compilador elimina la función miembro predeterminada.

class CopyBan
{
    // ...
    CopyBan(const CopyBan&)=delete;
    CopyBan& operator=(const CopyBan&)=delete;
    //...
};

2. Diseñe una clase que solo pueda crear objetos en el montón

Primero haga que el constructor de la clase sea privado y declare el constructor de copia como privado. Evite que otros llamen a la construcción de copias para generar objetos en la pila. Luego proporcione una función de miembro estático y complete la creación del objeto de montón en la función de miembro estático.

class HeapOnly
{
public:
	static HeapOnly* createOBJ()
	{
		return new HeapOnly;
	}
private:
	HeapOnly()
    {}
	HeapOnly(const HeapOnly&) = delete;
	
};

 También puede hacer que el destructor sea privado. Al liberar espacio, se puede liberar a través de la interfaz en la clase.


 3. Diseña una clase que solo pueda crear objetos en la pila

Como arriba, privatice el constructor y luego diseñe un método estático para crear el objeto y devolverlo. Cabe señalar aquí que la creación de objetos en otros lugares no puede prohibirse por completo.
 


4. Diseña una clase que no se pueda heredar

En C++98, la construcción de la clase base está privatizada, si el constructor de la clase base no se puede transferir a la clase derivada, no se puede heredar.

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
    static NonInherit GetInstance()
    {
        return NonInherit();
    }
private:
    NonInherit()
    {}
};

La palabra clave final en C++ 11, la clase modificada final, significa que la clase no se puede heredar.

class A final
{
// ....
};

5. Diseñe una clase que solo pueda crear un objeto (modo singleton)

Design Pattern (Patrón de diseño) es un resumen de un conjunto de experiencia de diseño de código que se usa repetidamente y es conocido por la mayoría de las personas.

El propósito de usar patrones de diseño: para la reutilización del código, para que el código sea más fácil de entender para otros y para garantizar la confiabilidad del código. Los patrones de diseño
hacen que la escritura de código sea verdaderamente ingeniería; los patrones de diseño son la piedra angular de la ingeniería de software, al igual que la estructura de un edificio.

Como el modo iterador, el modo adaptador (adaptador), el modo singleton, el modo de fábrica, el modo observador, etc. Estos son patrones de diseño.

  patrón único

Una clase solo puede crear un objeto, es decir, el modo singleton , que puede garantizar que solo haya una instancia de la clase en el sistema y proporcionar un punto de acceso global para acceder a él, que es compartido por todos los módulos del programa.

Por ejemplo, en un programa de servidor, la información de configuración del servidor se almacena en un archivo, y estos datos de configuración son leídos uniformemente por un objeto singleton, y luego otros objetos en el proceso de servicio obtienen la información de configuración a través de este objeto singleton, que Este enfoque simplifica la gestión de la configuración en entornos complejos.

Hay dos modos de implementación del modo singleton:

   modo hombre hambriento

Se crea un objeto de instancia único cuando se inicia el programa (antes de la función principal).

class Singleton
{
public:
	static Singleton& create()
	{
		return _sins;
	}
    
	void Insert(string s, int money)
	{
		//_info.insert(make_pair(s, money));
		_info[s] = money;
	}
	void Print()
	{
		for (const auto& kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
private:

	Singleton()
	{}
    Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	map<string, int> _info;
	static Singleton _sins;
};
Singleton Singleton::_sins;//初始化

 Se puede ver que, de hecho, solo hay un objeto, que no se puede copiar ni asignar.

Ventajas: 1. Sencillo
           2. Los objetos Singleton se usan con frecuencia en un entorno de alta concurrencia de subprocesos múltiples y tienen requisitos de alto rendimiento. Obviamente, es mejor usar el modo de hombre hambriento para evitar la competencia de recursos y mejorar la velocidad de respuesta.

Desventajas: 1. Hay demasiados datos cuando se inicializa el objeto singleton, lo que provoca un inicio lento;

           2. La inicialización de varias clases singleton tiene dependencias y el modo de hombre hambriento no se puede controlar. Porque la secuencia de inicio de múltiples instancias de objetos de clase singleton es incierta.

   modo perezoso

El objeto se creará después de la función principal, no afectará la secuencia de inicio y la secuencia de creación también se puede controlar activamente.

Si la construcción de un objeto singleton lleva mucho tiempo o consume muchos recursos, como cargar complementos, inicializar conexiones de red, leer archivos, etc., y es posible que el objeto no se utilice cuando el el programa se está ejecutando, entonces debe crearse al comienzo del programa Solo inicializando, hará que el programa se inicie muy lentamente. Así que es mejor usar el modo lento ( lazy loading ) en este caso.
//template<class lock>
//class lockGuard //智能指针->自动加锁,解锁	RAII方式
//{
//public:
//	lockGuard(lock& lk)
//		:_lk(lk)
//	{
//		_lk.lock();
//	}
//	~lockGuard()
//	{
//		_lk.unlock();
//	}
//private:
//	lock& _lk;
//};
//懒汉模式
class Singleton
{
public:
	//static Singleton& create()
	//{
	//	//第一次获取单列对象的时候创建对象
	//	//双检查加锁
	//	if (_psins == nullptr)//对象new出来以后,避免每次都加锁检查,提高性能
	//	{
	//		_mtx.lock();
	//		if (_psins == nullptr)//保证线程安全且只new一次
	//		{
	//			_psins = new Singleton;		//如果new抛异常,就没有解锁
	//		}
	//		_mtx.unlock();
	//	}
	//	return *_psins;
	//}
	
	static Singleton& create()
	{
		//第一次获取单列对象的时候创建对象
		//双检查加锁
		if (_psins == nullptr)//对象new出来以后,避免每次都加锁检查,提高性能
		{
			//lockGuard<mutex> mtx(_mtx);    //自己实现的
			std::lock_guard<mutex> mtx(_mtx);//库里提供的

			if (_psins == nullptr)//保证线程安全且只new一次
			{
				_psins = new Singleton;
			}
		}
		return *_psins;
	}
	//插入
	void Insert(string s, int money)
	{
		//_info.insert(make_pair(s, money));
		_info[s] = money;
	}

	void Print()
	{
		for (const auto& kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
	//一般单例对象不需要考虑释放
	//单例对象不用时,必须手动处理,一些资源需要保存
	static void DelInstance()
	{
		//保存数据到文件
		//...
		std::lock_guard<mutex> lock(_mtx);
		if (_psins)
		{
			delete _psins;
			_psins = nullptr;
		}
	}
	//自动回收
	class GC
	{
	public:
		~GC()
		{
			if (_psins)//如果你没有显示的释放,我就帮你回收
			{
				cout << "~GC()" << endl;
				DelInstance();	//内部类是外部类的友元,可以直接调用
			}
		}
	};
	
private:
	Singleton()
	{}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	map<string, int> _info;
	static Singleton* _psins;
	static mutex _mtx;
	static GC _gc;
};
Singleton* Singleton::_psins = nullptr;//初始化
mutex Singleton::_mtx;
Singleton::GC Singleton::_gc;

 

Ventajas: El objeto se crea cuando el objeto de instancia se usa por primera vez. El proceso comienza sin carga. Control libre de orden de inicio de múltiples instancias singleton
sistema.
Desventajas: complejo, hay que prestar atención a muchas cuestiones.

Código completo: clase especial/clase especial/prueba.cpp · El viento de la tarde no es tan bueno como tu sonrisa/deberes biblioteca-código nube-código abierto China (gitee.com)

expandir:

//懒汉模式
class Singleton
{
public:

	static Singleton& create()
	{
		static Singleton sins;    //只会创建一次
		return sins;
	}
	//插入
	void Insert(string s, int money)
	{
		//_info.insert(make_pair(s, money));
		_info[s] = money;
	}

	void Print()
	{
		for (const auto& kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
private:
	Singleton()
	{
		cout << "Singleton()" << endl;
	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
private:
	map<string, int> _info;
};

 Este también es un modo perezoso, porque las variables locales estáticas se crean e inicializan después de la función principal.

Antes de C++11, no se garantiza que la inicialización de pecados sea segura para subprocesos, pero después de C++11 sí lo está. Debido a que C++ 11 no estipuló esta área antes, cada implementación del compilador es diferente Después de C++ 11, se estipula que las variables locales estáticas deben ser seguras para subprocesos. Hay muchas instrucciones en Internet al respecto, puedes comprobarlo tú mismo.

Supongo que te gusta

Origin blog.csdn.net/weixin_68993573/article/details/129886764
Recomendado
Clasificación