PDA

Visualizza la versione completa : [C++] applicare una classe nella template class


monia82
03-08-2014, 16:44
Ciao a tutti,
ho un dubbio su come affrontare un'esercizio, è da un po' che ci provo, ma niente da fare. Forse ho interpretato male il professore... non so'.
Nella prima parte chiedeva di creare una rubrica utilizzando una template class con le funzioni di add, remove e print. Fin qua funziona tutto bene.

L'esercizio successivo, dice di implementare una classe Persona e applicarla con la template class appena creata (che ho chiamato ListaConcatenata.h).
Quindi ho creato un nuovo file .cpp e .h che si chiama persona, poi ho cercato di usarla nel file ListaConcatenata.h, ma mi da errore.

Metto anche una parte del codice del file ListaConcatenata.h:


#include <iostream>
#include <stdlib.h>
#include "persona.h" //QUESTA è LA PARTE CHE HO AGGIUNTO
using namespace std;

template <class T>
class LinkedList
{
private:
string nome;
string cognome;
string telefono;

Persona persona; //QUESTA è LA PARTE CHE HO AGGIUNTO

struct ListNode
{
T val1;
T val2;
T val3;
ListNode * next;
ListNode(T value1, T value2, T value3, ListNode * next1 = NULL)
{
val1 = value1;
val2 = value2;
val3 = value3;
next = next1;
}
};


public:
LinkedList() { head = NULL; }
~LinkedList();

void aggiungi();
void elimina();
void stampa();
};


e questa è la mia classe persona.h:



class Persona
{
public:
char nome[30];
char cognome[30];
char tel[30];


void add_nome( );
void add_cognome( );
void add_telefono( );
};



l'errore è
||=== Build: Debug in Rubrica (compiler: GNU GCC Compiler) ===|
include\persona.h|1|error: redefinition of 'class Persona'|
include\persona.h|1|error: previous definition of 'class Persona'|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

Cosa sto sbagliando? è sbagliato l'approccio?
Grazie.

MItaly
03-08-2014, 17:28
Probabilmente hai incluso il file Persona.h in più headers, con il risultato che nel .cpp finale di fatto la classe persona viene definita più volte. La soluzione standard per evitare le inclusioni multiple di un header è di usare i cosiddetti header guard: ogni .h dovrebbe avere il formato:


#ifndef NOME_FILE_H_INCLUDED // dove "NOME_FILE_H" è il nome del file .h in questione
#define NOME_FILE_H_INCLUDED

// qui il codice vero e proprio

#endif

Questo fa in modo che, se per un motivo o per l'altro il .h viene incluso più volte, solo nel primo caso viene effettivamente considerato il codice che contiene, evitando così errori di definizioni duplicate.

monia82
03-08-2014, 17:51
cavoli, era proprio quello il problema. Infatti non capivo quando serviva #ifndef e #endif in una classe.. ora è tutto chiaro.
Grazie mille

MItaly
03-08-2014, 23:13
:ciauz:

monia82
08-08-2014, 13:31
scusami, ma ho ancora un'altro dubbio, magari potresti illuminarmi!?! Consegnando l'esercizio, il professore mi ha risposto che lui si aspettava una cosa diversa nel main. Ti posto parte del codice del main:


#include <stdio.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include "persona.h"
#include "ListaConcatenata.h"

using namespace std;

int main()
{
ListaConcatenata<string> list;
....
}


Poi ad un certo punto nel main richiamavo la list.aggiungi() che è così implementata nella template class:


//************************************************** ******
// Funzione che aggiunge un nuovo contatto nella rubrica *
//************************************************** ******
template <class T>
void LinkedList<T>::aggiungi()
{
persona.add_nome();
nome=persona.nome;


persona.add_cognome();
cognome=persona.cognome;


persona.add_tel();
telefono=persona.numero;




if (head == NULL)
head = new Contatto(nome, cognome, telefono);
else
{
Contatto * nodoCorrente = head;
while (nodoCorrente->next != NULL)
nodoCorrente = nodoCorrente->next;

nodoCorrente->next = new Contatto(nome, cognome, telefono);
}
}


Tutto funziona correttamente, ma il professore dice che nel main si aspettava ListaConcatenata<Persona> list; Ho provato a mettere nel main il codice richiesto dal prof, ma giustamente, ho una valanga di errori perchè prima c'era la string, ora la classe Persona.... non capisco come correggere il programma, non so da dove partire!

Grazie mille.

MItaly
09-08-2014, 15:28
Leggendo il codice, mi sembra che tu abbia un po' di confusione sulle "responsabilità" della classe LinkedList... la classe LinkedList<T> è una classe generica (template), che deve occuparsi semplicemente di contenere oggetti di un generico tipo T; perché fa riferimento a Persona o a Contatto? L'unico tipo "esterno" a cui deve fare riferimento LinkedList è T, ovvero l'argomento template.

In altre parole: il punto dell'usare i template in questo caso è scrivere un contenitore che possa funzionare con qualunque tipo, senza rimanere legati a Persona o Contatto o quello che vuoi. Poi nel main o dove vuoi scriverai LinkedList<Contatto>, e ci potrai inserire dentro oggetti di tipo Contatto, che costituiranno la tua rubrica.

Poi: la classe LinkedList ha un'interfaccia che non ha senso. Come può essere che aggiungi ed elimina siano funzioni che non prendono alcun argomento? Da dove pescano l'elemento da aggiungere/eliminare? Un'interfaccia più sensata sarebbe del tipo:


void aggiungi(T elemento);
void elimina(T elemento); // su questo ci sarebbe da discutere, comunque...


Anche sulla classe Persona, non capisco perché hai i metodi add_nome e compagnia: o lasci i membri pubblici (e da fuori ci scrivi dentro quel che ti pare), oppure metti i membri privati e scrivi i metodi getter e setter (string get_nome()/void set_nome(string nome)). Dei metodi come add_nome (che peraltro non riceve alcun parametro? :confused: ) per una struttura contenente un numero fissato di record non hanno senso.

Io fossi in te ripartirei da capo, riordinandomi un po' le idee su cosa deve fare ciascuna classe, e cercando di capire quale dovrebbe essere l'interfaccia che dovrebbero esporre; se vuoi una mano in questo senso potrebbe essere utile avere la consegna completa del problema, visto che ci sono diverse cose che non mi tornano già così (come ci si aspetta che la print() faccia qualcosa di sensato su un tipo generico? come si dovrebbe scorrere una linked list con un'interfaccia di questo genere?).

monia82
09-08-2014, 17:07
Ciao MItaly,


In altre parole: il punto dell'usare i template in questo caso è scrivere un contenitore che possa funzionare con qualunque tipo, senza rimanere legati a Persona o Contatto o quello che vuoi. Poi nel main o dove vuoi scriverai LinkedList<Contatto>, e ci potrai inserire dentro oggetti di tipo Contatto, che costituiranno la tua rubrica.

ok, credo di aver capito l'errore concettuale che ho fatto...



Poi: la classe LinkedList ha un'interfaccia che non ha senso. Come può essere che aggiungi ed elimina siano funzioni che non prendono alcun argomento? Da dove pescano l'elemento da aggiungere/eliminare? Un'interfaccia più sensata sarebbe del tipo:


void aggiungi(T elemento);
void elimina(T elemento); // su questo ci sarebbe da discutere, comunque...


Anche sulla classe Persona, non capisco perché hai i metodi add_nome e compagnia: o lasci i membri pubblici (e da fuori ci scrivi dentro quel che ti pare), oppure metti i membri privati e scrivi i metodi getter e setter (string get_nome()/void set_nome(string nome)). Dei metodi come add_nome (che peraltro non riceve alcun parametro? :confused: ) per una struttura contenente un numero fissato di record non hanno senso.

non passavo alcun parametro perchè poi all'interno della funzione scrivevo delle cout e cin che mi chiedevano i valori, però collegandomi a quanto dicevi prima, non è corretto fare così perchè il template non sarebbe più generico, giusto!?

Credo che mi sono confusa le idee nel momento in cui mi è stato detto di creare una nuova classe Contatto da utilizzare nel template ListaContatto, non so più chi deve fare cosa. O meglio, immagino che le operazioni di aggiunta rimozione e stampa debba essere fatta dal template, ma a questo punto a cosa mi serve la classe Contatto!?

Scusa se sono un po' recidiva!!! ci sto provando... :messner:

MItaly
11-08-2014, 00:23
Ciao MItaly,
non passavo alcun parametro perchè poi all'interno della funzione scrivevo delle cout e cin che mi chiedevano i valori, però collegandomi a quanto dicevi prima, non è corretto fare così perchè il template non sarebbe più generico, giusto!?
Esatto. Concettualmente un container come LinkedList dovrebbe occuparsi esclusivamente di contenere gli elementi che gli viene detto di memorizzare "da fuori".


Credo che mi sono confusa le idee nel momento in cui mi è stato detto di creare una nuova classe Contatto da utilizzare nel template ListaContatto, non so più chi deve fare cosa. O meglio, immagino che le operazioni di aggiunta rimozione e stampa debba essere fatta dal template, ma a questo punto a cosa mi serve la classe Contatto!?

Non è molto chiaro neanche a me esattamente quello che ti è stato chiesto, per quello sarebbe utile che tu postassi il testo dell'esercizio. Una possibilità che mi viene in mente è che Contatto dovrebbe semplicemente contenere un oggetto Persona (che hai già, e che contiene nome e cognome) e ci aggiunge il numero di telefono. Ma, di nuovo, sarebbe meglio vedere il testo dell'esercizio, non vorrei indirizzarti in direzioni sbagliate.

monia82
11-08-2014, 14:56
Grazie MItaly,
il testo è questo:
realizzare una Template Class
template<class T>
class DblLinked
{
}
implementare aggiungi(T& contatto), rimuovi(T& contatto)e la stampa dell'intera lista (operatore <<)
Implementare una Classe Persona con dati uso rubrica telefonica e applicarla con la classe DblLinked

A questo punto mi sta venendo il dubbio anche su come ho creato la struttura per la persona, io l'avevo fatta così:

class Persona
{
public:
char nome[30];
char cognome[30];
char tel[30];

void add_nome( );
void add_cognome( );
void add_telefono( );
};



ma il prof, tramite email, mi ha detto che si aspettava una "chiave" formata da nome e cognome, e il restante dei dati formati dalla coppia attributo-valore, in modo tale che quando stampo viene fuori:
Mario Rossi
cellulare - 123456
cellulare2 - 987654
indirizzo - blablabla...
etc etc...

quindi devo fare qualcosa di più dinamico, giusto? così come l'ho struttato non va bene, cosa mi consigli di fare?
Grazie ancora per l'aiuto.

ps. sto andando off topic? devo creare una nuova discussione?

MItaly
11-08-2014, 22:29
Grazie MItaly,
il testo è questo:
realizzare una Template Class
template<class T>
class DblLinked
{
}
implementare aggiungi(T& contatto), rimuovi(T& contatto)e la stampa dell'intera lista (operatore <<)
Implementare una Classe Persona con dati uso rubrica telefonica e applicarla con la classe DblLinked
Ok, allora la cosa è piuttosto semplice.
La classe DblLinked, come previsto, non deve entrare nel merito, ma semplicemente fornire uno storage generico per il tipo T; la aggiungi andrà ad aggiungere un nodo in coda (o in testa) alla lista, copiandoci dentro il valore che ti viene passato (nota che qui `contatto` non è inteso come tipo, ma semplicemente come nome del parametro della aggiungi).

Analogamente, nella rimuovi andrai a cercare un nodo il cui elemento risulta uguale a quello che ti viene passato ed eliminerai il nodo corrispondente.

Nella stampa poi userai l'operatore << per stampare tutti gli elementi (scorri la lista e per ciascun elemento fai std::cout<<nodo.elemento).



[/I]A questo punto mi sta venendo il dubbio anche su come ho creato la struttura per la persona, io l'avevo fatta così:
[/FONT]
class Persona
{
public:
char nome[30];
char cognome[30];
char tel[30];

void add_nome( );
void add_cognome( );
void add_telefono( );
};


[FONT=arial]
ma il prof, tramite email, mi ha detto che si aspettava una "chiave" formata da nome e cognome, e il restante dei dati formati dalla coppia attributo-valore, in modo tale che quando stampo viene fuori:
Mario Rossi
cellulare - 123456
cellulare2 - 987654
indirizzo - blablabla...
etc etc...

Boh, per ora non starei a preoccuparmene, il testo del problema non mi sembra che menzioni nulla del genere, e una cosa del genere richiede un po' più di "magia" di quanto credo sappiate usare.

Per il resto, per Persona io mi aspetterei una cosa del tipo:


struct Persona
{
std::string nome;
std::string cognome;
std::string indirizzo;
std::string telefono1;
std::string telefono2;
};

(ammesso che possiate usare std::string, in caso contrario dovranno essere degli array di char).

Le altre due cose importanti da implementare a quel punto sono:

l'overload dell'operator== (altrimenti la lista non può sapere nella remove quando due elementi sono uguali);
l'overload dell'operator<< (altrimenti la lista non può stampare gli elementi nella stampa()).

Le due cose ti risultano più o meno note?

Loading