[C++] Comprender patrones de diseño, uso de stack&queue e implementación de simulación

1. Patrones de diseño

Un patrón de diseño es una solución a un problema recurrente en el diseño orientado a objetos . El término fue introducido en la informática desde el campo del diseño arquitectónico en la década de 1990 por Erich Gamma et al. El significado de este término aún se debate. Los algoritmos no son patrones de diseño porque están destinados a resolver problemas en lugar de diseñarlos. Un patrón de diseño generalmente describe un conjunto de clases y objetos que interactúan estrechamente entre sí. Los patrones de diseño proporcionan un lenguaje común para discutir el diseño de software, de modo que los novatos y otros diseñadores puedan dominar la experiencia de diseño de los diseñadores expertos. Los patrones de diseño también proporcionan objetivos para la refactorización de software.

En resumen, un patrón de diseño es un conjunto de soluciones exitosas o efectivas para un determinado tipo de problema recurrente.

Hay un total de 23 patrones de diseño, y los patrones de diseño a los que estamos expuestos actualmente son principalmente patrones de iterador .

Modo de iterador : después de encapsular el iterador, proporciona un método para acceder secuencialmente a cada elemento en un objeto agregado sin exponer los detalles subyacentes.

Entonces, a partir de este artículo, tenemos que entrar en contacto con un nuevo patrón de diseño: Patrón de adaptador (Patrón de adaptador)

Modo de adaptador : un adaptador es en realidad una conversión que convierte la interfaz de una clase en otra interfaz que el cliente desea, de modo que aquellas clases que no pudieron funcionar juntas debido a la incompatibilidad de la interfaz puedan trabajar juntas.

2. pila

1. El uso de la pila

1. La estructura de la pila.

imagen-20230425142636684

imagen-20230425142948683

Puedes ver que el parámetro de plantilla es diferente de la lista y el vector que vimos antes. El segundo parámetro de plantilla de la pila es un contenedor. También puedes ver en la siguiente introducción que la pila es un adaptador de contenedor . Esencialmente, proporciona una interfaz específica para este tipo de contenedor .

2. Interfaz de pila

imagen-20230425143534300

Se puede ver que la interfaz de la pila es mucho menor que la del contenedor. Estas interfaces son fáciles de usar, por lo que no las demostraré aquí. Si necesita ver la información detallada de la interfaz, se recomienda cplusplus.com aquí

interfaz ilustrar
pila construir una pila vacía
vacío Determine si la pila está vacía, devuelva el tipo bool
tamaño Devuelve el número de elementos en la pila, tipo size_t
arriba Devolver el elemento superior de la pila.
empujar Empuje la pila
estallido salir

2. Implementación de simulación de pila.

1. La estructura de la pila.

En lo anterior, vimos que hay dos parámetros de plantilla para la plantilla de clase de pila. El primero es el tipo de elemento de almacenamiento y el segundo es el tipo de contenedor. Aquí, el tipo de contenedor es consistente con la implementación en la biblioteca. El valor predeterminado contenedor es deque. El contenedor deque se introducirá

En la clase de pila, dado que se utilizan contenedores, solo se necesita un objeto contenedor para almacenar datos en las variables miembro, por lo que la estructura es la siguiente

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

2. Implementación de la interfaz

De acuerdo con la introducción de la interfaz anterior, implementamos la interfaz clave anterior. Dado que la pila usa otros contenedores, podemos llamar directamente a la interfaz del contenedor

//构造函数:我们不显示写,编译器生成。直接调用_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. cola

1. Uso de la cola

1. La estructura de la cola

imagen-20230425191254715

Al igual que la pila, la cola también es un adaptador de contenedor, pero las reglas son diferentes de la pila, la pila es el último en entrar, el primero en salir: LIFO, la cola es el primero en entrar, el primero en salir: FIFO .

3. interfaz de cola

imagen-20230425200154440

Las interfaces que son muy similares a stack no se explicarán una por una. Si es necesario, puede ir a cplusplus.com para ver documentos detallados.

2. Implementación de simulación de cola.

1. La estructura de la cola

Es muy similar a la estructura de la pila, y el marco del código es el siguiente:

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

2. Implementación de la interfaz

La implementación de la interfaz de la cola también es muy similar a la cola, lo único que debe cambiarse es la posición de los datos push y 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();
}

Hasta ahora, incluso podemos romper la pila y hacer cola a mano, jaja.

4. Entender deque

Notamos que los contenedores de pila y cola son deque. ¿Por qué es esto? Al aprender estructuras de datos, la pila y la cola implementadas generalmente se implementan mediante listas secuenciales y listas vinculadas. ¿Por qué no usar vector y listar aquí?

Tanto la estructura de vector como la de lista tienen algunas fallas: la verdadera deficiencia del vector radica en el consumo de expansión de capacidad y no admite la eliminación de complementos de encabezado, porque la eficiencia de eliminación en el medio del encabezado es baja. La lista no admite el acceso aleatorio, la tasa de aciertos de la memoria caché de la CPU es baja y el espacio no es continuo. Y deque tiene las ventajas de vector y list.Aunque deque también tiene muchas desventajas, el contenedor como adaptador simplemente las evita.

Para deque, solo necesitamos entender un poco, porque hay muy pocos escenarios de uso adecuados para deque.

1. Introducción al principio de deque

Deque (cola de doble extremo) : es una estructura de datos de espacio "continuo" de doble apertura. El significado de doble apertura es: las operaciones de inserción y eliminación se pueden realizar en ambos extremos de la cabeza y la cola, y la complejidad del tiempo es O (1) En comparación con el vector, la eficiencia del complemento es alta y no es necesario mover los elementos; en comparación con la lista, la tasa de utilización del espacio es relativamente alta.

imagen-20230425214336807

Este es solo un gráfico lógico, deque no es realmente un espacio continuo en la parte inferior

2. La estructura subyacente de deque

La capa inferior del deque se compone de muchos pequeños espacios consecutivos , similar a una matriz bidimensional, y las direcciones de estos pequeños espacios se almacenan en una matriz de punteros de control central. El diagrama esquemático de la estructura es el siguiente:

imagen-20230425220139343

3. Diseño del iterador Deque

Entonces, ¿cómo usa deque los iteradores para mantener esta estructura continua imaginaria?

Aquí hay una referencia a la imagen en el análisis del código fuente STL del Sr. Hou Jie:

imagen-20230425220410116

El iterador deque contiene cuatro punteros, donde cur apunta al valor actual, el primero y el último apuntan al pequeño espacio continuo donde se encuentra el cur actual y el nodo apunta a la dirección del espacio continuo almacenado en la matriz de control central.

4.Análisis de ventajas y desventajas de deque

Ventajas : cuando se inserta y elimina la cabeza, no hay necesidad de mover elementos, y la eficiencia es particularmente alta.Al expandir, no hay necesidad de mover una gran cantidad de elementos , por lo que su eficiencia es superior al vector. En comparación con la lista, su capa inferior es un espacio continuo, la tasa de utilización del espacio es relativamente alta y no es necesario almacenar campos adicionales.

Desventajas : no es adecuado para atravesar , porque al atravesar, el iterador de deque necesita verificar con frecuencia si se mueve al límite de un cierto espacio pequeño , lo que resulta en una baja eficiencia. En escenarios secuenciales, es posible que se requiera un recorrido frecuente, por lo que en la práctica, cuando se requiere una estructura lineal, en la mayoría de los casos, se da prioridad al vector y la lista. No hay muchas aplicaciones de deque, y una aplicación que se puede ver hasta ahora es que STL lo usa como la estructura de datos subyacente de la pila y la cola.

5. ¿Por qué elegir deque como el contenedor predeterminado subyacente de pila y cola?

Stack es una estructura de datos lineal especial de último en entrar, primero en salir, por lo que siempre que tenga una estructura lineal con operaciones push_back() y pop_back() , se puede usar como el contenedor subyacente de la pila, como vector y lista; la cola es una estructura de datos lineal especial de primero en entrar, primero en salir, siempre que tenga una estructura lineal con operaciones push_back y pop_front , se puede usar como el contenedor subyacente de la cola, como una lista. Sin embargo, en STL, deque se selecciona como contenedor subyacente de forma predeterminada para la pila y la cola, principalmente porque:

  1. No es necesario atravesar la pila y la cola (por lo que la pila y la cola no tienen iteradores), y solo deben operar en uno o ambos extremos del fijo.
  2. Cuando los elementos en la pila crecen, deque es más eficiente que el vector (no es necesario mover una gran cantidad de datos al expandirse); cuando los elementos en la cola crecen, deque no solo es eficiente, sino que también tiene un alto uso de memoria.
  3. Combina las ventajas de deque y evita perfectamente sus defectos.

Y queue elige deque como su contenedor subyacente de forma predeterminada, principalmente porque:

  1. No es necesario atravesar la pila y la cola (por lo que la pila y la cola no tienen iteradores), y solo deben operar en uno o ambos extremos del fijo.
  2. Cuando los elementos en la pila crecen, deque es más eficiente que el vector (no es necesario mover una gran cantidad de datos al expandirse); cuando los elementos en la cola crecen, deque no solo es eficiente, sino que también tiene un alto uso de memoria.
  3. Combina las ventajas de deque y evita perfectamente sus defectos.

Supongo que te gusta

Origin blog.csdn.net/weixin_63249832/article/details/130375466
Recomendado
Clasificación