Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Problemas con listas y templates

Iniciado por Tarzan, 02 de Diciembre de 2007, 12:15:26 PM

« anterior - próximo »

Tarzan

Buenas a todos/as,

LLevo varios días con un problema de compilación en C++ al utilizar una plantilla para implementar una lista enlazada.

Tengo creada la plantilla <TIPO> para la clase Nodo y para la Clase Lista.
Ambas clases compilan bien y pienso que el código es correcto.

Por otra parte tengo una clase Cliente y pretendo realizar una listas de clientes usando las 3 clases.

En el main tengo el siguiente Código:

int main(int argc, char* argv[])
{
   Cliente *cliente = new Cliente ("Pepe");  
   Lista <Cliente> lista;
   lista.insertarAlPrincipio(*cliente);
   
   cout << "DNI" << lista.daInicio().daDNI() << endl;

   return 0;
}


se supone que la función daInicio() de la clase Lista devuelve un objeto TIPO. Esta es la función:

template <class TIPO>
TIPO Lista<TIPO>::daInicio() {
   return this->inicio;
}


cuando escribo lista.daInicio().  nada más escribir el punto me aparece un cuadro (utilidad del Borland) con todos los métodos de la clase Cliente. Es decir, el compilador sabe que el objeto devuelto por la función daInicio() es de tipo Cliente...
Pero al compilar el proyecto me da un error:

[C++ Error] Lista.cpp(46): E2034 Cannot convert 'Nodo<Cliente> *' to 'Cliente'

Y no sé qué hacer... He probado a definir la lista así:
Lista <Cliente *> lista; pero nada... sigue fallando... Alguien sabe de qué se está quejando el compilador y como puedo arreglarlo?... Muchas gracias.

Warchief

Seguro que this->inicio es un Cliente y no un Node<Cliente>* ?

// Lo tienes así?
Nodo<Cliente>* inicio;

Entonces no es un Cliente, sino un nodo de cliente, por lo que tienes que extraer el cliente del nodo (sin ver la clase nodo, no podemos saber cómo lo has hecho).

Tarzan

Sí Warchief!!... Yo también pienso que debe ser eso...
A ver.. Un lista tiene dos referencias a Nodo y el Nodo es el que contiene el objeto <TIPO> y otra referencia a otro Nodo.

Pongo el código:

Nodo.h
template<class TIPO> class Lista;
template <class TIPO>

class Nodo {

   protected:

       TIPO objeto;
       Nodo <TIPO> *siguiente;
       Nodo <TIPO> *anterior;

       friend class Lista<TIPO>;

   public:

       // Constructor
       Nodo(TIPO obj, Nodo<TIPO> *sig = NULL, Nodo<TIPO> *ant = NULL) :
            objeto(obj), siguiente(sig), anterior(ant) {}
       Nodo();
       TIPO daObjeto();
       void ponObjeto(TIPO);
};


Lista.h

#ifndef ListaH
#define ListaH
#include "Nodo.h"

template <class TIPO>

class Lista {

   protected:

       Nodo<TIPO> *inicio;
       Nodo<TIPO> *fin;

   public:

       Lista():inicio(NULL),fin(NULL) {}
       ~Lista();

       void insertarAlPrincipio(TIPO); // Inserta al principio de la lista
      void insertarAlFinal(TIPO); // Inserta al final de la lista
       bool isListaVacia();
      int eliminarNodo(TIPO); // Borra uno o mas nodos
      void eliminarNodos(); // Borra todos los nodos
       TIPO daInicio();


};
#endif


y el Main es el que he puesto antes.
Creo que sé lo que dices de que debería recoger el Cliente desde el atributo Objeto del nodo y no desde la lista pero, en el main, la función daInicio() ya me dice que el objeto devuelto es un cliente y no un nodo...
Imagino que será por la declaración esa de Lista <Cliente> lista; .

¿Cómo puedo hacerlo?... Cómo puedo introducir un objeto Cliente en el atributo objeto del Nodo e insertarlo en la lista?.

Warchief

Las templates no compilan hasta que las utilizas. El hecho de que no muestren los errores antes no significa que estén bien implementadas, sino que puede ser que no estés usando el método. Al añadir la llamada al método, te compila la template (con el tipo ya instanciado) y verifica los errores, mostrándote el que tienes.

La implementación de daInicio debería ser algo como:


TIPO daInicio() {
 // return this->inicio; // MAL
 return this->inicio->daObjeto(); // mejor,
 // aunque no comprueba que inicio!=0 (lo que fallaría si no tienes elementos)
}


PD: En tu ejemplo, ten en cuenta que estás almacenando una copia del Cliente, y no el cliente en sí [Lista<Cliente> vs Lista<Cliente*>], por lo que cambios en el cliente que has llamado "Pepe", no se verán reflejados en el elemento que has insertado en la lista.

Tarzan

Pues sí, la verdad es que entiendo perfectamente lo que dices. Desde la clase lista, con la función daInicio() obtengo el nodo y, desde la clase nodo, con la función daObjeto(), obtengo el Cliente en este caso.

Sin embargo, cuando desde el Main() utilizo la lista.daInicio(). nada más escribir el punto el compilador me muestra todas las funciones de la clase Cliente y no del nodo... es decir que no puedo poner daObjeto() o no sé como hacerlo...

Tal vez usando un casting?... es decir:

((Nodo) lista.daInicio()).daObjeto()

o algo así...

Otra cosa... las funciones daInicio (de la clase lista) y daObjeto() (de la clase Nodo), ambas devuelven un objeto de la plantilla TIPO... eN el primer caso será tratado como un Nodo y en el segundo, según lo que se almacene en la lista (en este caso un Cliente)...

¿Son objetos TIPO en ambos caso, verdad?... nada de TIPO*...


Muchas gracias por atenderme Warchief!!

Warchief

No te has fijado bien:

TIPO daInicio() {  
 return this->inicio->daObjeto();
}


Eso es la implementación correcta de daInicio. Intellisense te pone los métodos de Cliente porque el tipo de retorno es "TIPO", que es "Cliente".
Dos opciones:
a) O pones la implementación de daInicio como arriba
b) O pones Nodo<TIPO> como el retorno  [ Nodo<TIPO> daInicio() { ... } ]

Tarzan

Siiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii!!!!

Qué bienn!!! Muchísimas gracias!!!

Ya funciona!!... He implementado la función daInicio() según la opción A. LLamando al daObjeto() del Nodo y ya funciona...
Ahora sólo me falta una cosilla..

Resulta que, desde el Main, estoy obligado a incluir el Lista.cpp y el Nodo.cpp porque, de no hacerlo, el linker me da un error de que no encuentra los métodos de esas clases...
Yo siempre acostumbro a incluir sólo los .h pero, en este caso, no hay forma de que el linker deje de quejarse.

Estos son los errores:

[Linker Error] Unresolved external 'Lista<Cliente>::~Lista<Cliente>()' referenced from C:\UTILIDADES\PROGRAMACION\BORLAND6\PROJECTS\MSDOSGINE\MAIN.OBJ

[Linker Error] Unresolved external 'Lista<Cliente>::insertarAlPrincipio(Cliente)' referenced from C:\UTILIDADES\PROGRAMACION\BORLAND6\PROJECTS\MSDOSGINE\MAIN.OBJ

[Linker Error] Unresolved external 'Lista<Cliente>::daInicio()' referenced from C:\UTILIDADES\PROGRAMACION\BORLAND6\PROJECTS\MSDOSGINE\MAIN.OBJ


He creado un proyecto del tipo "Console Wizard", es decir, sin GUI (Interfaz gráfica), y no tengo referecias cruzadas con los includes...
Utilizo el Borland c++ 6.0.
La verdad es que funciona si escribo @include "Lista.cpp" y #include "Nodo.cpp" en el Main pero no me gusta porque debería funcionar al incluir los .h...

¿Sabes a qué puede ser debido?

De nuevo un millón de gracias por la ayuda prestada!!!

Warchief

Las templates tienen que ir en un único fichero. Mete la implementación también en el .h, tanto para la lista como para el nodo.

Tarzan

Ok Warchief,

Al final he creado un Proyecto nuevo con GUI y funciona perfectamente.
El linker se quejaba con el tipo de Proyecto de Consola pero a mí me interesa más el que dispone de interfaz gráfica... Así que perfecto.


De momento he dejado un par de archivos (.h y .cpp) para el Nodo y otro par para la Lista.
Me gusta tenerlo separado porque me ayuda a localizar zonas del código.

Muchísimas gracias por todo!!

Ahora a seguir programando!!

Yoshi.css

Un consejo cuando trabajes con plantillas: primero implementa las clases que serán plantillas como si no lo fueran, y si todo va bien, entonces la conviertes.

Como dice Warchief, las plantillas se compilan "al vuelo" en tiempo de ejecución, y si algo está mal el problema no aparecerá hasta que no se produzca. Además, el que no de problemas no significa que esté exento de fallos. Un método en una clase que tenga un fallo no aparecerá hasta que utilices expresamente ese método.

Ésta forma de hacer las cosas da un poco más de trabajo, pero te aseguro que es el que menos tiempo te llevará como empieces con errores en las plantillas. Y ya ni te cuento si hablamos si se producen en listas, colas, pilas,....

Salu2.

tamat

Cita de: "Yoshi.css"Como dice Warchief, las plantillas se compilan "al vuelo" en tiempo de ejecución,...
MAL
Por un stratos menos tenso

Warchief


Yoshi.css

Cita de: "Warchief"Las templates no compilan hasta que las utilizas. El hecho de que no muestren los errores antes no significa que estén bien implementadas, sino que puede ser que no estés usando el método.
Te pido disculpas Warchief, interpreté "hasta que las utilizas" como en tiempo de ejecución cuando, obviamente, puedes utilizarlas en tiempo de compilación.

Tamat tiene razón, lo que he dicho es una burrada y está mal. Hay un tiempo de compilación y un tiempo de ejecución, por lo que "compilar en tiempo de ejecución" es imposible.

Lo lamento.

Warchief

Na, no era necesario disculparse hombre. Sólo aclarar que en efecto se compilan con el resto de código, pero si no las utilizas en el código, el compilador no tiene necesidad (ni puede) de interpretar el tipo de la template.

Buffon

un apunte y sólo uno que estoy en el trabajo xDDDD

Cliente * cliente = new Cliente("Pepe");

esto no es java






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.