2008-12-14 29 views
6

Necesito usar listas para mi programa y necesito decidir si uso std :: vector o std :: list. El problema con el vector es que no hay un método de eliminación y con la lista que no hay operador []. Así que decidí escribir mi propia clase extendiendo std :: list y sobrecargando el operador [].Extendiendo std :: list

Mi código es el siguiente:

#include <list> 

template <class T > 
class myList : public std::list<T> 
{ 
public: 
T operator[](int index); 
T operator[](int & index); 
myList(void); 
~myList(void); 
}; 

#include "myList.h" 

template<class T> 
myList<T>::myList(void): std::list<T>() {} 

template<class T> 
myList<T>::~myList(void) 
{ 
std::list<T>::~list(); 
} 

template<class T> 
T myList<T>::operator[](int index) { 
int count = 0; 
std::list<T>::iterator itr = this->begin(); 
while(count != index)itr++; 
return *itr;  
} 

template<class T> 
T myList<T>::operator[](int & index) { 
int count = 0; 
std::list<T>::iterator itr = this->begin(); 
while(count != index)itr++; 
return *itr; 
} 

puedo compilarlo pero me da un error de vinculador si trato de usarlo. ¿Algunas ideas?

+0

Tienes 'operador T [] (int index);' y 'operador T [] (int & index);' pero puede desea usar 'T & operator [] (int index)' y 'const T & operator [] (int index) const' en su lugar. Además, dentro de esas funciones puede hacer 'while (index -)' en lugar de crear una nueva variable para realizar un seguimiento del recuento. – Dennis

Respuesta

10

Todo el código de la plantilla debe colocarse en el archivo de encabezado. Esta solución de relleno vincula problemas (esa es la manera más simple). La razón por la que sucede es porque los compiladores compilan cada archivo fuente (.cc) por separado de otros archivos. Por otro lado, necesita saber qué código exactamente necesita crear (es decir, en qué se sustituye la plantilla T en) y no tiene otra forma de saberlo a menos que el programador lo diga explícitamente o incluya todo el código cuando la plantilla instanciación sucede. Es decir. cuando mylist.cc está compilado, no sabe nada sobre los usuarios de mylist y qué código necesita crearse. Por otro lado, si listuser.cc está compilado y todo el código mylist está presente, el compilador crea el código mylist necesario. Puede leer más sobre él en here o en Stroustrup.

Su código tiene problemas, ¿qué pasa si el usuario lo solicita negativo o demasiado grande (más que la cantidad de elementos en la lista). Y no miré demasiado.

Además, no sé cómo tienes pensado usarlo, pero su operador [] es un tiempo O (N), lo que conducirá probablemente fácilmente a O (N * N) bucles ...

+1

"su operador [] es hora O (N)" - esta es exactamente la razón por la cual no está incluida en la 'std :: list <>' del estándar. –

6

Los vectores tienen erase method que pueden eliminar elementos. ¿No es eso suficiente?

+0

No es tan fácil de usar como el método remove en std :: list –

+0

¿Entonces usa std :: remove en su lugar? –

+0

No sabía de eso. Voy a verlo –

1

Tienes que mover todo el código de tu plantilla al encabezado.

21

Dado el enunciado original del problema,

Necesito usar listas para mi programa y necesito decidir si utilizo std :: vector o std :: list. El problema con el vector es que no hay un método de eliminación y con la lista que no hay operador [].

no hay necesidad de crear su propia clase lista (esto no es una opción de diseño inteligente de todos modos, porque std::list no tiene un destructor virtual, que es un fuerte indicio de que no está destinado a ser utilizado como una clase base).

Aún puede lograr lo que quiere usando std::vector y la función std::remove. Si v es una std::vector<T>, a continuación, para quitar el valor value, simplemente hay que escribir:

#include <vector> 
#include <algorithm> 
T value = ...; // whatever 
v.erase(std::remove(v.begin(), v.end(), value), v.end()); 
+0

Debería ser: v.erase (std :: remove (v.begin(), v.end(), value), vec.end()); – dalle

+0

Tienes razón, por supuesto. Gracias. – ChrisN

+0

Y una versión de elemento único: v.erase (std :: find (...)); –

1

las cosas obvias ya se ha descrito en detalle:

Pero los métodos a los que decide aplicar ??

  • Destructor.
    • No es necesario que el compilador genere eso para usted.
  • Las dos versiones diferentes del operador [] no tienen sentido
    • también debe ser std :: :: Lista de uisng size_type como el índice
    • A menos que la intención de apoyar a los índices negativos.
  • No hay versiones const de operador []
  • Si se va a aplicar [] también se debe hacer en el()
  • se ha perdido todas las diferentes maneras de construir una lista.
  • Los recipientes deben definir varios tipos internamente
5

Además de otros comentarios excelentes, la mejor manera de extender un contenedor estándar no es por derivación, pero la escritura de funciones gratuitas. Por ejemplo, vea cómo se puede usar Boost String Algorithms para extender std::string y otras clases de cadena.

0

No hay necesidad de llamar a destructor de std :: list, porque usted ya deriva de std :: list cuando destructor pidió myList automáticamente std :: list destructor será llamado.

+0

tenga en cuenta que std :: list no tiene un destructor virtual. – gbjbaanb

+0

La falta del destructor virtual sería un problema solo si intentara eliminar un objeto de myList a través de un puntero a std :: list, lo que de todos modos sería un uso realmente extraño de un contenedor. –

+0

cierto, pero vale la pena recordarlo de todos modos. – gbjbaanb

52

Dependiendo de sus necesidades, debe usar std::vector (si necesita agregar/eliminar a menudo al final, y acceso aleatorio), o std::deque (si lo necesita a menudo agrega/quita al final o al principio, y su el conjunto de datos es enorme, y todavía quiere acceso aleatorio). Aquí es un buen cuadro que muestra cómo tomar la decisión:

Container Choice http://adrinael.net/containerchoice.png

+1

¡Eso es genial, especialmente si se tomó el tiempo para hacerlo! +1 – Samaursa

Cuestiones relacionadas