[C++] Comprendre les modèles de conception, l'utilisation de la pile et de la file d'attente et l'implémentation de la simulation

1. Modèles de conception

Un patron de conception est une solution à un problème récurrent dans la conception orientée objet . Le terme a été introduit en informatique à partir du domaine de la conception architecturale dans les années 1990 par Erich Gamma et al. La signification de ce terme est encore débattue. Les algorithmes ne sont pas des modèles de conception car les algorithmes sont destinés à résoudre des problèmes plutôt qu'à les concevoir. Un modèle de conception décrit généralement un ensemble de classes et d'objets qui interagissent étroitement les uns avec les autres. Les modèles de conception fournissent un langage commun pour discuter de la conception de logiciels afin que l'expérience de conception des concepteurs qualifiés puisse être maîtrisée par les novices et les autres concepteurs. Les modèles de conception fournissent également des objectifs pour la refactorisation logicielle.

En bref, un modèle de conception est un ensemble de solutions réussies ou efficaces à un certain type de problème récurrent

Il existe un total de 23 modèles de conception, et les modèles de conception auxquels nous sommes actuellement exposés sont principalement des modèles d'itérateur .

Mode itérateur : une fois l'itérateur encapsulé, il fournit une méthode pour accéder séquentiellement à chaque élément d'un objet agrégé sans exposer les détails sous-jacents

Donc à partir de cet article, nous devons entrer en contact avec un nouveau design pattern - Adapter Pattern (Adapter Pattern)

Mode adaptateur : un adaptateur est en fait une conversion qui convertit l'interface d'une classe en une autre interface souhaitée par le client, afin que les classes qui ne pouvaient pas fonctionner ensemble en raison d'une incompatibilité d'interface puissent fonctionner ensemble

2.pile

1. L'utilisation de la pile

1. La structure de la pile

image-20230425142636684

image-20230425142948683

Vous pouvez voir que le paramètre de modèle est différent de la liste et du vecteur que nous avons vus précédemment. Le deuxième paramètre de modèle de la pile est un conteneur. Vous pouvez également voir dans l'introduction suivante que la pile est un adaptateur de conteneur . Essentiellement, il fournit une interface spécifique pour ce type de conteneur .

2. Interface de pile

image-20230425143534300

On voit que l'interface de la pile est bien inférieure à celle du conteneur. Ces interfaces sont faciles à utiliser, je ne vais donc pas les démontrer ici. Si vous avez besoin de voir les informations détaillées de l'interface, cplusplus.com est recommandé ici

interface illustrer
empiler construire une pile vide
vide Détermine si la pile est vide, renvoie le type booléen
taille Renvoie le nombre d'éléments dans la pile, type size_t
haut Renvoie l'élément supérieur de la pile
pousser Poussez la pile
populaire sortir

2. Mise en œuvre de la simulation de la pile

1. La structure de la pile

Dans ce qui précède, nous avons vu qu'il existe deux paramètres de modèle pour le modèle de classe de pile. Le premier est le type d'élément de stockage et le second est le type de conteneur. Ici, le type de conteneur est cohérent avec l'implémentation dans la bibliothèque. La valeur par défaut conteneur est deque. Le conteneur deque sera introduit

Dans la classe pile, puisque les conteneurs sont utilisés, un seul objet conteneur est nécessaire pour stocker les données dans les variables membres, la structure est donc la suivante

namespace zht//这里把实现的stack包在命名空间中
{
    
    
	template<class T, class Container = std::deque<T>>
	class stack
	{
    
    
	public:
		//成员函数
	private:
		Container _con;
	};
}

2. Mise en œuvre de l'interface

Selon l'introduction de l'interface ci-dessus, nous implémentons l'interface clé ci-dessus. Puisque la pile utilise d'autres conteneurs, nous pouvons appeler directement l'interface du conteneur

//构造函数:我们不显示写,编译器生成。直接调用_con的默认构造
bool empty() const
{
    
    
    return _con.empty();
}
size_t size() const
{
    
    
    return _con.size();
}
const T& top() 
{
    
    
    return _con.back();//适配这类操作的容器都是支持back或者front接口的,直接调用即可
}
void push(const T& val)
{
    
    
    _con.push_back(val);
}
void pop()
{
    
    
    _con.pop_back();
}

3.file d'attente

1. Utilisation de la file d'attente

1. La structure de la file d'attente

image-20230425191254715

Identique à la pile, la file d'attente est également un adaptateur de conteneur, mais les règles sont différentes de la pile, la pile est le dernier entré, premier sorti : LIFO, la file d'attente est le premier entré, premier sorti : FIFO .

3.interface de file d'attente

image-20230425200154440

Les interfaces qui ressemblent beaucoup à stack ne seront pas expliquées une à une, si nécessaire, vous pouvez vous rendre sur cplusplus.com pour consulter des documents détaillés.

2. Mise en œuvre de simulation de file d'attente

1. La structure de la file d'attente

Il est très similaire à la structure de la pile et le cadre de code est le suivant :

namespace zht//这里把实现的queue包在命名空间中
{
    
    
	template<class T, class Container = std::deque<T>>
	class queue
	{
    
    
	public:
		//成员函数
	private:
		Container _con;
	};
}

2. Mise en œuvre de l'interface

L'implémentation de l'interface de la file d'attente est également très similaire à la file d'attente, la seule chose qui doit être modifiée est la position des données push et pop

//构造函数:我们不显示写,编译器生成。直接调用_con的默认构造
bool empty() const
{
    
    
    return _con.empty();
}
size_t size() const
{
    
    
    return _con.size();
}
const T& top() 
{
    
    
    return _con.back();//适配这类操作的容器都是支持back或者front接口的,直接调用即可
}
void push(const T& val)//这里的push我们采用尾插的方式插入
{
    
    
    _con.push_back(val);
}
void pop()//由于push使用的是尾插,所以pop采用头删的方式
{
    
    
    _con.pop_back();
}

Jusqu'à présent, nous pouvons même déchirer la pile et faire la queue à la main haha.

4. Comprendre deque

Nous avons remarqué que les conteneurs de pile et de file d'attente sont décalés. Pourquoi est-ce? Lors de l'apprentissage des structures de données, la pile et la file d'attente implémentées sont généralement implémentées à l'aide de listes séquentielles et de listes chaînées. Pourquoi ne pas utiliser vecteur et liste ici ?

Les structures de vecteur et de liste présentent toutes deux des défauts : le véritable défaut du vecteur réside dans la consommation d'extension de capacité et ne prend pas en charge la suppression du connecteur d'en-tête, car l'efficacité de la suppression au milieu de l'en-tête est faible. La liste ne prend pas en charge l'accès aléatoire, le taux de réussite du cache du processeur est faible et l'espace n'est pas continu. Et deque présente à la fois les avantages du vecteur et de la liste.Bien que deque présente également de nombreux inconvénients, le conteneur en tant qu'adaptateur les évite simplement.

Pour deque, nous avons juste besoin de comprendre un peu, car il existe très peu de scénarios d'utilisation appropriés pour deque.

1. Introduction au principe de deque

Deque (file d'attente à double extrémité) : il s'agit d'une structure de données d'espace "continu" à double ouverture. La signification de la double ouverture est : les opérations d'insertion et de suppression peuvent être effectuées aux deux extrémités de la tête et de la queue, et la complexité temporelle est O(1).Par rapport au vecteur, l'efficacité du plug-in est élevée et les éléments n'ont pas besoin d'être déplacés ; par rapport à la liste, le taux d'utilisation de l'espace est relativement élevé.

image-20230425214336807

Ceci est juste un graphique logique, deque n'est pas vraiment un espace continu en bas

2. La structure sous-jacente de deque

La couche inférieure du deque est composée de nombreux petits espaces consécutifs , similaires à un tableau à deux dimensions, et les adresses de ces petits espaces sont stockées dans un tableau de pointeurs de contrôle central. Le schéma de principe de la structure est le suivant :

image-20230425220139343

3.Deque conception d'itérateur

Alors, comment deque utilise-t-il les itérateurs pour maintenir cette structure continue imaginaire ?

Voici une référence à l'image dans l'analyse du code source STL de M. Hou Jie :

image-20230425220410116

L'itérateur deque contient quatre pointeurs, où cur pointe sur la valeur actuelle, le premier et le dernier point sur le petit espace continu où se trouve le cur actuel et le nœud pointe sur l'adresse de l'espace continu stocké dans le tableau de contrôle central.

4.Analyse des avantages et des inconvénients de deque

Avantages : Lorsque la tête est insérée et supprimée, il n'est pas nécessaire de déplacer des éléments et l'efficacité est particulièrement élevée. Lors de l'expansion, il n'est pas nécessaire de déplacer un grand nombre d'éléments , son efficacité est donc supérieure à celle du vecteur. Par rapport à la liste, sa couche inférieure est un espace continu, le taux d'utilisation de l'espace est relativement élevé et aucun champ supplémentaire ne doit être stocké.

Inconvénients : ne convient pas à la traversée , car lors de la traversée, l'itérateur de deque doit fréquemment vérifier s'il se déplace à la limite d'un certain petit espace , ce qui entraîne une faible efficacité. Dans les scénarios séquentiels, une traversée fréquente peut être nécessaire, donc en pratique, lorsqu'une structure linéaire est requise, dans la plupart des cas, le vecteur et la liste sont prioritaires.Il n'y a pas beaucoup d'applications de deque, et une application qui peut être vue jusqu'à présent est que STL l'utilise comme structure de données sous-jacente de la pile et de la file d'attente

5. Pourquoi choisir deque comme conteneur par défaut sous-jacent de la pile et de la file d'attente

Stack est une structure de données linéaire spéciale dernier entré, premier sorti, donc tant qu'elle a une structure linéaire avec les opérations push_back() et pop_back() , elle peut être utilisée comme conteneur sous-jacent de la pile, comme vector et liste ; file d'attente est un premier entré premier sorti spécial Une structure de données linéaire, tant qu'elle a une structure linéaire avec des opérations push_back et pop_front , peut être utilisée comme conteneur sous-jacent de la file d'attente, telle que liste. Cependant, dans STL, deque est sélectionné comme conteneur sous-jacent par défaut pour la pile et la file d'attente, principalement parce que :

  1. La pile et la file d'attente n'ont pas besoin d'être traversées (donc la pile et la file d'attente n'ont pas d'itérateurs) et n'ont besoin de fonctionner qu'à une ou aux deux extrémités du fixe.
  2. Lorsque les éléments de la pile augmentent, deque est plus efficace que vector (pas besoin de déplacer beaucoup de données lors de l'expansion) ; lorsque les éléments de la file d'attente augmentent, deque est non seulement efficace, mais utilise également beaucoup de mémoire.
  3. Combine les avantages de deque, et évite parfaitement ses défauts

Et la file d'attente choisit deque comme conteneur sous-jacent par défaut, principalement parce que :

  1. La pile et la file d'attente n'ont pas besoin d'être traversées (donc la pile et la file d'attente n'ont pas d'itérateurs) et n'ont besoin de fonctionner qu'à une ou aux deux extrémités du fixe.
  2. Lorsque les éléments de la pile augmentent, deque est plus efficace que vector (pas besoin de déplacer beaucoup de données lors de l'expansion) ; lorsque les éléments de la file d'attente augmentent, deque est non seulement efficace, mais utilise également beaucoup de mémoire.
  3. Combine les avantages de deque, et évite parfaitement ses défauts

Je suppose que tu aimes

Origine blog.csdn.net/weixin_63249832/article/details/130375466
conseillé
Classement