2010-10-05 18 views
43

¿Cuál es el significado de programación genérica en C++?¿Cuál es el significado de "programación genérica" ​​en C++?

Además, estoy tratando de averiguar qué contenedor , iterador, y diferentes tipos de ellos significan.

+15

Oigan los que votan negativamente, háganle saber el motivo ... Al menos no lo repetirá para la próxima vez .. – liaK

+10

¿Por qué votar a la baja? Es una pregunta válida para hacer. ¡SO no es solo para expertos! –

+3

La pregunta es muy breve, pero también creo que sigue siendo válida. La "programación genérica", los "contenedores" y los "iteradores" son conceptos muy importantes y pueden ser intimidantes para un principiante. @bunty Intenta hacer que la pregunta se parezca más a una pregunta SO "real". – WolfgangA

Respuesta

45

programación genérica significa que usted no está escribiendo código fuente que se compila tal cual, sino que se escribe "plantillas" de los códigos fuente que el compilador en el proceso de compilación se transforma en códigos fuente. El ejemplo más simple para programación genérica es clases de contenedor como matrices, listas o mapas que contienen una colección de otros objetos. Pero hay mucho más para la programación genérica. En el contexto de C++ (y llamado meta programación) significa escribir programas que se evalúan en tiempo de compilación.

Un ejemplo básico de la programación genérica son las plantillas de contenedores: En un lenguaje de tipos estáticos como C++ que tendría que declarar recipientes separados que contienen números enteros, flotadores, y otros tipos o tratar con punteros a void y por lo tanto la pérdida de toda la información de tipo . Las plantillas que son la forma C++ de programación genérica aprovechan esta restricción al permitirle definir clases donde uno o más parámetros no están especificados en el momento de definir la clase. Cuando instancia la plantilla más tarde le dice al compilador qué tipo debe usar para crear la clase de la plantilla. Ejemplo:

template<typename T> 
class MyContainer 
{ 
    // Container that deals with an arbitrary type T 
}; 

void main() 
{ 
    // Make MyContainer take just ints. 
    MyContainer<int> intContainer; 
} 

Las plantillas son genérico porque el compilador traduce la plantilla en código real. Tenga en cuenta que en el caso de que no cree una instancia de su plantilla, no se generará ningún código en todo. Por otro lado, si declara MyContainer<int>, MyContainer<float> y MyContainer<String>, el compilador creará tres versiones de su código, cada una de las cuales tiene un tipo diferente. Habrá algunas optimizaciones involucradas, pero básicamente su código de plantilla se instanciará a tres nuevos tipos.

Iteradores son un patrón de diseño que se popularizó en el libro "Design Patterns" seminales por Gamma et al. Es un patrón para iterar sobre el contenido de una clase de contenedor. A diferencia del uso de un for, un iterador es una instancia de una clase que apunta a un miembro del contenedor y le proporciona una interfaz unificada para recorrer el contenedor y acceder a los miembros. Tome vistazo a este ejemplo:

// Instanciate template QList with type int 
QList<int> myList; 

// put some ints into myList 

// Copyconstruct iterator that points to the 
// first member of the list. 
QList<int>::iterator i = myList.begin(); 

// Iterate through the list 
while (i != myList.end()) { 
    std::cout << *i << std::endl; 
    i++; 
} 

En este ejemplo de C++ que estoy instantating una plantilla QList con el tipo int. QLista una clase de contenedor que almacena una lista de objetos.En este ejemplo, lo usaremos para almacenar enteros.

Luego creo un iteradori para recorrer la lista. myList.begin() devuelve un iterador que apunta al primer elemento de la lista. Podemos comparar el iterador con otro iterador myList.end() que apunta a después de el último elemento de la lista. Si ambos iteradores son iguales, sabemos que hemos pasado el último elemento. En el ciclo estamos imprimiendo el elemento accediéndolo con *i y vamos al siguiente elemento con i++.

Tenga en cuenta que en este ejemplo * y ++ son operadores sobrecargados y reimplementados por la clase de iterador. En un lenguaje de programación sin sobrecarga del operador, podría haber métodos como i.element() o i.next() que realizan la misma tarea. Es importante ver que i no es un puntero sino una clase completa que solo imita el comportamiento de un puntero.

¿Cuál es el beneficio de los iteradores? Proporcionan una forma unificada para acceder a los miembros de una clase de contenedor, completamente independiente de cómo se implementa la clase de contenedor internamente. No importa si quiere recorrer una lista, mapa o árbol, las clases de iterador (deberían) siempre funcionan de la misma manera.

+0

Buena respuesta; Añadiría que los iteradores te permiten diseñar y usar algoritmos genéricos (ver el encabezado STL ) que se pueden aplicar a cualquier contenedor que proporcione alguna funcionalidad a los iteradores (por ejemplo, se puede incrementar, mover en cualquier punto de la secuencia, disminuir, ... .). –

+0

@Matteo ltalia Gracias ..... – bunty

+2

No estoy totalmente de acuerdo. La Programación Genérica es un paradigma propio y la Plantilla es una forma de implementarlos en C++. La programación genérica no implica meta-programación, como ha demostrado Go. –

0

programación genérica: prácticamente solo implica plantillas.

contenedor: Una estructura o clase, que contiene sus propios datos y métodos que actúan sobre esos datos.

iterador: es un puntero a alguna dirección de memoria que puede recorrer (como una matriz).

Corrígeme si está equivocado en cualquiera de los anteriores.

+2

Corregiría su definición de iterador, para decir que es una * generalización * de punteros a matrices. –

+0

@Oli: De acuerdo. Su interfaz es una dirección de memoria iterable, pero eso no es lo que realmente son en la mayoría de los casos (listas, mapas, etc.). – Puppy

+0

Me gustaría definir que Iterator es una forma estandarizada de acceder a los bloques de STL Container al abstraer el código del llamante del tipo de contenedor. –

1

Como punto de interés histórico, las versiones de C++ que venían antes de que las plantillas fueran parte del lenguaje tenían un "generic.h" que contenía macros de preprocesador que podían expandirse a declaraciones de clase. Por lo tanto, podría tener un esquema genérico ("plantilla") para una clase que podría variar al pasar ciertos parámetros a las macros cuando los expandió a declaraciones de clase reales. Sin embargo, las macros de preprocesador no son de tipo seguro y un poco torpe de manejar, y su uso en el código de C++ se redujo significativamente debido a estas razones; C++ adoptó las plantillas más versátiles como elementos del lenguaje, pero el término programación "genérica" ​​siguió vivo. Los "genéricos" se usan ahora en otros lenguajes de programación como moldes de tipos glorificados. Aparte de eso, la pregunta ya ha sido respondida por expertos.

13

Container

En C++, un contenedor es una clase que le permite almacenar objetos. Por ejemplo, la biblioteca estándar std::vector<T> es una matriz redimensionable que almacena objetos de algún tipo T. Para que se considere formalmente una clase de contenedor, debe exponer ciertas funcionalidades para facilitar la programación genérica. Podría citar los requisitos exactos de la norma C++, pero para la mayoría de los propósitos, las clases de contenedores que importan son los de la biblioteca estándar: vector, deque, list, map, set y multimap/multiset.

Uno de los requisitos importantes es que deben permitir iterator acceso.

iterador

"iterador" puede significar dos cosas aquí: Es el nombre de un patrón de diseño, pero en C++ es también el nombre de una expresión específica de ese patrón de diseño. Un iterador de C++ es un tipo que permite el cruce sobre una secuencia de elementos utilizando una sintaxis similar a un puntero.

Por ejemplo, si tiene una matriz int a[10], puede utilizar un puntero claro como un iterador:

int* first = a; // create an iterator that points to the beginning of the array 
++first; // make the iterator point to the second element 
int i = *first; // get the value of the element pointed to by the iterator 
int* last = a+10; //create an "end" iterator, one which points one past the end of the array 

Si tuviera una lista enlazada, como std::list<int> l, podría hacer lo mismo, aunque ahora mis iteradores ya no son sólo los punteros, pero en lugar de un tipo de clase implementado para trabajar específicamente con std::list:

std::list<int>::iterator first = l.begin(); // create an iterator that points to the beginning of the list 
++first; // make the iterator point to the second element 
int i = *first; // get the value of the element pointed to by the iterator 
std::list<int>::iterator last = l.end(); //create an "end" iterator, one which points one past the end of the list 

o con un vector std::vector<int> v:

std::vector<int>::iterator first = v.begin(); // create an iterator that points to the beginning of the vector 
++first; // make the iterator point to the second element 
int i = *first; // get the value of the element pointed to by the iterator 
std::list<int>::iterator last = v.end(); //create an "end" iterator, one which points one past the end of the vector 

Lo importante de iteradores es que nos dan una sintaxis uniforme para atravesar las secuencias de elementos, independientemente de cómo la secuencia se almacena en la memoria (o incluso si se almacena en la memoria. Se podría escribir un iterador para iterar sobre los contenidos de una base de datos en el disco. O podemos usar envolturas de iterador para hacer una corriente como std::cin mirada como una secuencia de objetos demasiado:

std::istream_iterator<int>(std::cin) first; 
    ++first; // make the iterator point to the second element 
    int i = *first; // get the value of the element pointed to by the iterator 
    std::list<int>::iterator last; //create an "end" iterator, which marks the end of the stream 

aunque debido a esto se ajusta un flujo regular, es un tipo más limitado de iterador (no se puede mover al revés, por ejemplo, lo que significa que no todos los siguientes algoritmos funcionan con iteradores de flujo.

Ahora, dado cualquiera de estos tipos de iteradores, podemos utilizar todos los algoritmos de la biblioteca estándar que están diseñados para trabajar con iteradores. para encontrar el primer elemento en la secuencia con el valor 4:

std::find(first, last, 4); // return the first iterator which equals 4 and which is located in the interval [first, last) 

O podemos ordenar la secuencia (no funciona con corriente iteradores):

std::sort(first, last); 

o si escribimos una función que un int cuadrados, como este:

int square(int i) { return i * i; } 

entonces podemos aplicarlo a toda la secuencia:

// for every element in the range [first, last), apply the square function, and output the result into the sequence starting with first 
std::transform(first, last, first, square); 

Esa es la ventaja de iteradores: se abstraer los detalles del contendrá er, para que podamos aplicar operaciones genéricas en cualquier secuencia. Gracias a los iteradores, la misma implementación de find o sort funciona con listas enlazadas y matrices, o incluso con sus propias clases de contenedores de fabricación casera.

programación genérica

programación genérica es básicamente la idea de que el código debe ser lo más genérico posible. Como se muestra en los ejemplos del iterador anterior, se nos ocurre un conjunto común de funciones que un tipo debe admitir para llamarse un iterador, y luego escribimos algoritmos que funcionan con cualquier tipo de iterador.

Compare esto con la programación tradicional orientada a objetos, donde los iteradores tendrían que "probar" que son iteradores al heredar de algún tipo de interfaz IIterator. Eso nos evitaría el uso de punteros crudos como iteradores, por lo que perderíamos genericidad.

En C++, con programación genérica, no necesitamos la interfaz oficial. Simplemente escribimos los algoritmos usando plantillas, por lo que aceptan cualquier tipo de que parezca se parezca a un iterador, independientemente de dónde, cuándo y cómo se definen, y si derivan o no de una clase base común o interfaz

5

En la definición más simple, la programación genérica es un estilo de programación de computadoras en el que los algoritmos se escriben en términos de tipos a ser especificados y luego se crean instancias cuando se necesitan para tipos específicos provistos como parámetros.

+1

debe elaborar su respuesta. Ya hay muchas respuestas agradables en el hilo. . – CyberBoy

+4

acaba de explicar por qué mi respuesta es breve – cseav

0

concepto de parámetros de tipo, que permiten diseñar clases y métodos que difieren la especificación de uno o más tipos hasta que la clase o método se declara e instancia por código de cliente.

Cuestiones relacionadas