C++ Avancé—Conception de classe spéciale

Table des matières

1. Concevoir une classe qui ne peut pas être copiée

2. Concevez une classe qui ne peut créer que des objets sur le tas

 3. Concevez une classe qui ne peut créer que des objets sur la pile

4. Concevez une classe qui ne peut pas être héritée

5. Concevez une classe qui ne peut créer qu'un seul objet (mode singleton)

  motif singleton

   mode homme affamé

   mode paresseux


 

1. Concevoir une classe qui ne peut pas être copiée

La copie n'apparaîtra que dans deux scénarios : la surcharge du constructeur de copie et de l'opérateur d'affectation. Par conséquent, si vous souhaitez qu'une classe interdise la copie, il vous suffit de rendre la classe incapable d'appeler la surcharge du constructeur de copie et de l'opérateur d'affectation.

En C++98, seules la construction de copie et la surcharge d'affectation sont déclarées, non implémentées, et ces deux fonctions membres peuvent être définies comme privées. Après avoir défini private, il ne peut pas être appelé en dehors de la classe.Avec la déclaration, le compilateur ne le générera pas par défaut.

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

L'utilisation de delete est étendue en C++ 11. En plus de libérer les ressources demandées par new, si delete suit la fonction membre par défaut avec =delete, cela signifie que le compilateur supprime la fonction membre par défaut.

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

2. Concevez une classe qui ne peut créer que des objets sur le tas

Commencez par rendre le constructeur de la classe private et déclarez le constructeur de copie comme private. Empêchez les autres d'appeler la construction de copie pour générer des objets sur la pile. Fournissez ensuite une fonction membre statique et terminez la création de l'objet de tas dans la fonction membre statique.

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

 Vous pouvez également rendre le destructeur privé. Lors de la libération d'espace, il peut être libéré via l'interface de la classe.


 3. Concevez une classe qui ne peut créer que des objets sur la pile

Comme ci-dessus, privatisez le constructeur, puis concevez une méthode statique pour créer l'objet et le renvoyer.Il convient de noter ici que la création d'objets à d'autres endroits ne peut pas être complètement interdite.
 


4. Concevez une classe qui ne peut pas être héritée

En C++98, la construction de la classe de base est privatisée, si le constructeur de la classe de base ne peut pas être transféré dans la classe dérivée, il ne peut pas être hérité.

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

Le mot-clé final en C++11, la classe modifiée finale, signifie que la classe ne peut pas être héritée.

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

5. Concevez une classe qui ne peut créer qu'un seul objet (mode singleton)

Design Pattern (Design Pattern) est un résumé d'un ensemble d'expériences de conception de code qui est utilisé à plusieurs reprises et connu de la plupart des gens.

Le but de l'utilisation de modèles de conception : pour la réutilisation du code, pour rendre le code plus facile à comprendre pour les autres et pour garantir la fiabilité du code. Les modèles de conception
font de l'écriture de code une véritable ingénierie ; les modèles de conception sont la pierre angulaire de l'ingénierie logicielle, tout comme la structure d'un bâtiment.

Tels que le mode itérateur, le mode adaptateur (adaptateur), le mode singleton, le mode usine, le mode observateur, etc. Ce sont des modèles de conception.

  motif singleton

Une classe ne peut créer qu'un seul objet, c'est-à-dire le mode singleton , qui peut garantir qu'il n'y a qu'une seule instance de la classe dans le système et fournir un point d'accès global pour y accéder, qui est partagé par tous les modules du programme.

Par exemple, dans un programme serveur, les informations de configuration du serveur sont stockées dans un fichier, et ces données de configuration sont lues uniformément par un objet singleton, puis d'autres objets du processus de service obtiennent les informations de configuration via cet objet singleton, qui is Cette approche simplifie la gestion de la configuration dans des environnements complexes.

Il existe deux modes d'implémentation du mode singleton :

   mode homme affamé

Un objet d'instance unique est créé au démarrage du programme (avant la fonction principale).

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;//初始化

 On peut voir qu'il n'y a en effet qu'un seul objet, qui ne peut pas être copié et assigné.

Avantages : 1. Simplicité
           2. Les objets singleton sont fréquemment utilisés dans un environnement multithread à forte concurrence et ont des exigences de performances élevées. Évidemment, il est préférable d'utiliser le mode homme affamé pour éviter la concurrence des ressources et améliorer la vitesse de réponse.

Inconvénients : 1. Il y a trop de données lorsque l'objet singleton est initialisé, ce qui ralentit le démarrage ;

           2. L'initialisation de plusieurs classes singleton a des dépendances et le mode homme affamé ne peut pas être contrôlé. Parce que la séquence de démarrage de plusieurs instances d'objets de classe singleton est incertaine.

   mode paresseux

L'objet sera créé après la fonction principale, il n'affectera pas la séquence de démarrage et la séquence de création peut également être activement contrôlée.

Si la construction d'un objet singleton prend beaucoup de temps ou consomme beaucoup de ressources, comme le chargement de plug-ins, l'initialisation des connexions réseau, la lecture de fichiers, etc., et qu'il est possible que l'objet ne soit pas utilisé lorsque le programme est en cours d'exécution, alors il doit être créé au début du programme Juste en initialisant, le programme démarrera très lentement. Il est donc préférable d'utiliser le mode paresseux ( lazy loading ) dans ce cas.
//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;

 

Avantages : L'objet est créé lors de la première utilisation de l'objet d'instance. Le processus démarre sans charge. Contrôle sans ordre de démarrage de plusieurs instances singleton
système.
Inconvénients : complexe, nécessité de prêter attention à de nombreux problèmes.

Code complet : classe spéciale/classe spéciale/test.cpp · Le vent du soir n'est pas aussi bon que ton sourire/devoirs bibliothèque-code cloud-open source Chine (gitee.com)

développer:

//懒汉模式
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;
};

 C'est également un mode paresseux, car les variables locales statiques sont créées et initialisées après la fonction principale.

Avant C++11, il n'est pas garanti que l'initialisation de sins soit thread-safe, mais après C++11, elle est garantie. Étant donné que C++11 ne stipulait pas ce domaine auparavant, chaque implémentation de compilateur est différente.Après C++11, il est stipulé que les variables locales statiques doivent être thread-safe. Il existe de nombreuses instructions sur Internet à cet égard, vous pouvez les vérifier vous-même.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_68993573/article/details/129886764
conseillé
Classement