Visualizzazione dei risultati da 1 a 5 su 5
  1. #1

    [C++] Aiuto programma funzioni virtuali ed elaborazione file

    Salve, sono nuovo del forum, spero possiate aiutarmi
    Il mio problema è che quando eseguo il file eseguibile del programma che allego mi esce una scritta: "This application has requested the runtime..." quando il programma chiama una funzione virtuale pura. Inoltre ho alcuni problemi quando faccio l'input da file. Allego la traccia e il codice.

    Traccia:
    Un veterinario deve organizzare, per il suo ambulatorio, l’agenda giornaliera delle visite. Per semplicità si supponga che i clienti di tale ambulatorio siano solo cani e gatti. Si progetti la classe animale e da essa si derivino le classi cane e gatto come illustrato in figura.
    La segretaria del veterinario riceve le prenotazioni telefonicamente ed immette da tastiera, su file sequenziale, in apposito record: il nome dell’animale, il tipo, la razza, l’età, la data della visita (senza l’ora per rendere le cose più semplici), il sesso ed il motivo. Alla fine della giornata la segretaria stampa la lista delle prenotazioni ricevute ordinate secondo la tabella seguente ( che deve essere l’output del vostro programma).
    Si organizzi dunque un main program nel quale si possano immettere tutti i dati necessari alla gestione dell’ambulatorio ed alla fine invochi la funzione di stampa che apra il file ambulatorio.txt, lo visiti estragga i dati e li stampi in maniera ordinata (crescente secondo la data)
    L’allievo organizzi, il programma secondo files separati ed in particolare:
    1. il file UsaAmbulatori.cpp per la funzione main,
    2. il file Ambulatorio.cpp per le funzioni membro e per le altre funzioni che l’allievo intende eventualmente sviluppare,
    3. il file Ambulatorio.h per le dichiarazioni.
    L’allievo utilizzi, se necessario, le funzioni di libreria messe a disposizione dal linguaggio anche per l’ordinamento.

    Codice:
    codice:
    /*
     * ambulatorio.h
     *
     * 
     *      
     */
    
    #ifndef AMBULATORIO_H_
    #define AMBULATORIO_H_
    
    using namespace std;
    
    struct Record{
    	char* data_visita;
    	char* nome;
    	char* tipo;
    	char* razza;
    	char sesso;
    	int eta;
    	char* motivo;
    };
    
    class Animale{
    public:
    	Animale(char* ="", int=0, char=' ');
    	~Animale();
    	char* get_nome()const{return nome;}
    	int get_eta()const{return eta;}
    	char get_sesso()const{return sesso;}
    	virtual char* get_razza()const =0;
    	virtual void stampa();
    protected:
    	char* nome;
    	int eta;
    	char sesso;
    };
    
    class Cane: public Animale{
    public:
    	Cane(char* ="", int=0, char=' ', char* ="");
    	~Cane();
    	virtual char* get_razza()const{return razza;}
    	virtual void stampa();
    private:
    	char* razza;
    };
    
    class Gatto: public Animale{
    public:
    	Gatto(char* ="", int=0, char=' ', char* ="");
    	~Gatto();
    	virtual char* get_razza()const{return razza;}
    	virtual void stampa();
    private:
    	char* razza;
    };
    
    void crea_animale(Record& );
    void crea_record(Record&, Animale*);
    void output_file(const Record&, char*, int& );
    
    void stampa_dati(char*, const int);
    void input_file(Record&, ifstream& );
    bool change_record(Record&, Record& );
    void print_record(Record& );
    
    #endif /* AMBULATORIO_H_ */
    codice:
    /*
     * ambulatorio.cpp
     *
     * 
     */
    
    #include <iostream>
    #include <fstream>
    #include <iomanip>
    #include <string.h>
    #include "ambulatorio.h"
    
    using namespace std;
    
    Animale::Animale(char* name, int age, char gender){
    	nome=new char[strlen(name)+1];
    	strcpy(nome, name);
    	eta=age;
    	sesso=gender;
    }
    
    Animale::~Animale(){
    	delete[] nome;
    }
    
    void Animale::stampa(){
    	cout << "\nnome animale: " << nome;
    	cout << "\neta': " << eta;
    	cout << "\nsesso: " << sesso << endl;
    }
    
    Cane::Cane(char* name, int age, char gender, char* breed):
    	Animale(name, age, gender){
    	razza=new char[strlen(breed)+1];
    	strcpy(razza, breed);
    }
    
    Cane::~Cane(){
    	delete[] razza;
    }
    
    void Cane::stampa(){
    	Animale::stampa();
    	cout << "razza: " << razza << endl;
    }
    
    Gatto::Gatto(char* name, int age, char gender, char* breed):
    	Animale(name, age, gender){
    	razza=new char[strlen(breed)+1];
    	strcpy(razza, breed);
    }
    
    Gatto::~Gatto(){
    	delete[] razza;
    }
    
    void Gatto::stampa(){
    	Animale::stampa();
    	cout << "razza: " << razza << endl;
    }
    
    void crea_animale(Record& r){
    	int a_opt;
    	int g_opt;
    	Animale* ptr;
    	char* name=new char[12];
    	int age;
    	char gender;
    	char* breed=new char[12];
    	cout << "\nInserisci nome: ";
    	cin >> setw(12) >> name;
    	cout << "Inserisci eta': ";
    	cin >> age;
    	cout << "Se l'animale e' maschio inserisci 0, se e' femmina 1: ";
    	do{
    		cin >> g_opt;
    		if(g_opt==0) gender='M';
    		else if(g_opt==1) gender='F';
    		else cout << "Opzione non valida, reinseriscila: ";
    	}while(g_opt!=0 && g_opt!=1);
    	cout << "Inserisci razza: ";
    	cin >> setw(12) >> breed;
    	cout << "Se vuoi inserire un cane inserisci 0, se vuoi inserire un gatto inserisci 1: ";
    	do{
    		cin >> a_opt;
    		if(a_opt==0){
    			Cane c(name, age, gender, breed);
    			cout << "elemento creato:";
    			c.stampa();
    			r.tipo="Cane";
    			ptr=&c;
    		}else if(a_opt==1){
    			Gatto g(name, age, gender, breed);
    			cout << "elemento creato:";
    			g.stampa();
    			r.tipo="Gatto";
    			ptr=&g;
    		}else cout << "Opzione non valida, reinseriscila: ";
    	}while(a_opt!=0 && a_opt!=1);
    	crea_record(r, ptr);
    }
    
    void crea_record(Record& r, Animale* ptr){
    	//copia nome
    	r.nome=new char[strlen((ptr->get_nome())+1)];
    	strcpy(r.nome, ptr->get_nome());
    	//copia eta'
    	r.eta=ptr->get_eta();
    	//copia sesso
    	r.sesso=ptr->get_sesso();
    	//copia razza
    	r.razza=new char[strlen((ptr->get_razza())+1)];
    	strcpy(r.razza, ptr->get_razza());
    	//inserisce data
    	char gg[3], mm[3], aa[5];
    	cout << "Inserisci data: ";
    	cin >> setw(3) >> gg;
    	cin.ignore();
    	cin >> setw(3) >> mm;
    	cin.ignore();
    	cin >> setw(5) >> aa;
    	r.data_visita=new char[11];
    	strcpy(r.data_visita, gg);
    	strcat(r.data_visita, "/");
    	strcat(r.data_visita, mm);
    	strcat(r.data_visita, "/");
    	strcat(r.data_visita, aa);
    	//inserisce motivazione
    	cout << "Inserisci motivazione: ";
    	r.motivo=new char[20];
    	cin >> setw(20) >> r.motivo;
    }
    
    void output_file(const Record& r, char* name1, int& n_record){
    	ofstream f;
    	f.open(name1, ios::app);
    	if(!f){
    		cout << "\nFile non creato!\n";
    		return;
    	}
    	f << r.data_visita << "\n";
    	f << r.nome << "\n";
    	f << r.tipo << "\n";
    	f << r.razza << "\n";
    	f << r.sesso << "\n";
    	f << r.eta << "\n";
    	f << r.motivo << "\n\n\n";
    	f.close();
    	n_record++;
    }
    
    void stampa_dati(char* name1, const int n_record){
    	ifstream f;
    	Record vet[n_record];
    	f.open(name1, ios::in);
    	if(!f){
    		cout << "\nFile non aperto!\n";
    		return;
    	}
    	int i=0;
    	while(!f.eof()){
    		input_file(vet[i], f);
    		i++;
    	}
    	f.close();
    	for(int j=0;j<n_record-1;j++)
    		for(int k=0;k<n_record-1;k++)
    			if(change_record(vet[k], vet[k+1])){
    				Record temp=vet[k];
    				vet[k]=vet[k+1];
    				vet[k+1]=temp;
    			}
    	cout << "\n\n\n" << setiosflags(ios::left) << setw(11) << "Giorno" << setw(12) << "Nome" << setw(6) << "Tipo" <<
    			setw(12) << "Razza" << setw(6) << "Sesso" << setw(5) << "Eta'" << setw(20) << "Motivo" << resetiosflags(ios::left) << endl;
    	for(i=0;i<n_record;i++)
    		print_record(vet[i]);
    	cout << endl;
    }
    
    void input_file(Record& r, ifstream& file){
    	file >> r.data_visita;
    	file >> r.nome;
    	file >> r.tipo;
    	file >> r.razza;
    	file >> r.sesso;
    	file >> r.eta;
    	file >> r.motivo;
    }
    
    bool change_record(Record& r1, Record& r2){
    	if(strncmp(&(r1.data_visita)[6], &(r2.data_visita)[6], 4)>0)
    		return true;
    	if(strncmp(&(r1.data_visita)[6], &(r2.data_visita)[6], 4)==0 && strncmp(&(r1.data_visita)[3], &(r2.data_visita)[3], 2)>0)
    		return true;
    	if(strncmp(&(r1.data_visita)[6], &(r1.data_visita)[6], 4)==0 && strncmp(&(r1.data_visita)[3], &(r2.data_visita)[3], 2)==0 && strncmp(r1.data_visita, r2.data_visita, 2)>0)
    		return true;
    	return false;
    }
    
    void print_record(Record& r){
    	cout << setiosflags(ios::left) << setw(11) << r.data_visita << setw(12) << r.nome << setw(6) << r.tipo <<
    			setw(12) << r.razza << setw(6) << r.sesso << setw(5) << r.eta << setw(20) << r.motivo << resetiosflags(ios::left) << endl;
    }
    codice:
    /*
     * usaambulatorio.cpp
     *
     */
    
    #include <iostream>
    #include <fstream>
    #include <stdlib.h>
    #include "ambulatorio.h"
    
    #define nome1 "C:\\Users\\Pippo\\Documents\\ambulatorio.txt"
    
    using namespace std;
    
    int main(){
    	ofstream f(nome1, ios::trunc);
    	f.close();
    	Record r;
    	int num_entry=0;
    	int opt;
    	cout << "Programma di test Ambulatorio\n\n\n";
    	do{
    		cout << "\n\n\n1)Inserisci animale su file\n2)Stampa dati da file in modo ordinato\n3)Esci\nScegli un'opzione: ";
    		cin >> opt;
    		switch(opt){
    		case 1:{
    			crea_animale(r);
    			output_file(r, nome1, num_entry);
    			break;
    		}
    		case 2:{
    			stampa_dati(nome1, num_entry);
    			break;
    		}
    		case 3:{
    			break;
    		}
    		default:{
    			cout << "Opzione non valida!\n\n";
    			break;
    		}
    		}
    	}while(opt!=3);
    	system("PAUSE");
    	return 0;
    }
    Per favore rispondete.

  2. #2
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Intanto il distruttore di Animale dev'essere dichiarato virtual, altrimenti non saranno richiamati i distruttori delle classi derivate.
    Poi in input_file immetti dati nella struttura senza prima aver allocato spazio per contenerli e questo è male.

    Altra osservazione: nelle derivate utilizzi [/b]char* razza[/b]. Visto che è un dato comune (e Animale non è un'interfaccia), meglio metterlo nella classe base.

    Infine:
    L’allievo utilizzi, se necessario, le funzioni di libreria messe a disposizione dal linguaggio
    direi di usare std::string invece dei char*, im dodo da gestire le stringhe in maniera "umana" ed evitare problemi con allocazioni dimenticate. Tanto più che non effettui una scrittura binaria sul file, ma scrivi i vari dati uno per linea.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  3. #3
    Riprendo la discussione perchè non mi è chiaro il motivo per cui la funzione virtuale pura genera un errore nel programma. Io penso di averla implementata bene. Riposto il codice:

    codice:
    class Animale{
    public:
    	Animale(char* ="", int=0, char=' ');
    	virtual ~Animale();
    	char* get_nome()const{return nome;}
    	int get_eta()const{return eta;}
    	char get_sesso()const{return sesso;}
    	virtual char* get_razza()const=0;
    	virtual void stampa();
    protected:
    	char* nome;
    	int eta;
    	char sesso;
    };
    
    class Cane: public Animale{
    public:
    	Cane(char* ="", int=0, char=' ', char* ="");
    	virtual ~Cane();
    	virtual char* get_razza()const{return razza;}
    	virtual void stampa();
    private:
    	char* razza;
    };
    
    class Gatto: public Animale{
    public:
    	Gatto(char* ="", int=0, char=' ', char* ="");
    	virtual ~Gatto();
    	virtual char* get_razza()const{return razza;}
    	virtual void stampa();
    private:
    	char* razza;
    };
    codice:
    Animale::Animale(char* name, int age, char gender){
    	nome=new char[strlen(name)+1];
    	strcpy(nome, name);
    	eta=age;
    	sesso=gender;
    }
    
    Animale::~Animale(){
    	delete[] nome;
    }
    
    void Animale::stampa(){
    	cout << "\nnome animale: " << nome;
    	cout << "\neta': " << eta;
    	cout << "\nsesso: " << sesso << endl;
    }
    
    Cane::Cane(char* name, int age, char gender, char* breed):
    	Animale(name, age, gender){
    	razza=new char[strlen(breed)+1];
    	strcpy(razza, breed);
    }
    
    Cane::~Cane(){
    	delete[] razza;
    }
    
    void Cane::stampa(){
    	Animale::stampa();
    	cout << "razza: " << razza << endl;
    }
    
    Gatto::Gatto(char* name, int age, char gender, char* breed):
    	Animale(name, age, gender){
    	razza=new char[strlen(breed)+1];
    	strcpy(razza, breed);
    }
    
    Gatto::~Gatto(){
    	delete[] razza;
    }
    
    void Gatto::stampa(){
    	Animale::stampa();
    	cout << "razza: " << razza << endl;
    }
    
    void crea_animale(Record& r){
    	int a_opt;
    	int g_opt;
    	Animale* ptr=0;
    	char* name=new char[12];
    	int age;
    	char gender;
    	char* breed=new char[12];
    	cout << "\nInserisci nome: ";
    	cin >> setw(12) >> name;
    	cout << "Inserisci eta': ";
    	cin >> age;
    	cout << "Se l'animale e' maschio inserisci 0, se e' femmina 1: ";
    	do{
    		cin >> g_opt;
    		if(g_opt==0) gender='M';
    		else if(g_opt==1) gender='F';
    		else cout << "Opzione non valida, reinseriscila: ";
    	}while(g_opt!=0 && g_opt!=1);
    	cout << "Inserisci razza: ";
    	cin >> setw(12) >> breed;
    	cout << "Se vuoi inserire un cane inserisci 0, se vuoi inserire un gatto inserisci 1: ";
    	do{
    		cin >> a_opt;
    		if(a_opt==0){
    			Cane c(name, age, gender, breed);
    			cout << "elemento creato:";
    			c.stampa();
    			r.tipo="Cane";
    			Cane* c_ptr=&c;
    			ptr=c_ptr;
    		}else if(a_opt==1){
    			Gatto g(name, age, gender, breed);
    			cout << "elemento creato:";
    			g.stampa();
    			r.tipo="Gatto";
    			Gatto* g_ptr=&g;
    			ptr=g_ptr;
    		}else cout << "Opzione non valida, reinseriscila: ";
    	}while(a_opt!=0 && a_opt!=1);
    	crea_record(r, ptr);
    }
    
    void crea_record(Record& r, Animale* ptr){
    	//copia nome
    	r.nome=new char[strlen(ptr->get_nome())+1];
    	strcpy(r.nome, (ptr->get_nome()));
    	//copia eta'
    	r.eta=ptr->get_eta();
    	//copia sesso
    	r.sesso=ptr->get_sesso();
    	//copia razza
    	r.razza=new char[strlen(ptr->get_razza())+1];
    	strcpy(r.razza, (ptr->get_razza()));
    	//inserisce data
    	char gg[3], mm[3], aa[5];
    	cout << "Inserisci data: ";
    	cin >> setw(3) >> gg;
    	cin.ignore();
    	cin >> setw(3) >> mm;
    	cin.ignore();
    	cin >> setw(5) >> aa;
    	r.data_visita=new char[11];
    	strcpy(r.data_visita, gg);
    	strcat(r.data_visita, "/");
    	strcat(r.data_visita, mm);
    	strcat(r.data_visita, "/");
    	strcat(r.data_visita, aa);
    	//inserisce motivazione
    	cout << "Inserisci motivazione: ";
    	r.motivo=new char[20];
    	cin >> setw(20) >> r.motivo;
    }
    Non posso cambiare i parametri perchè devo seguire il testo della traccia.

    Se definisco la classe Animale come virtuale non pura il programma non copia bene il nome e la razza all'interno del record, mentre i parametri come eta e sesso li copia bene.

  4. #4
    Ho risolto. L'errore è nel costrutto if. Infatti istanzio un oggetto cane all'interno dell' if, ma quando esco dal costrutto quest'oggetto viene deallocato, e la funzione crea_record(r, ptr) prende un puntatore che non punta a nulla!

    codice:
    do{
    		cin >> a_opt;
    		if(a_opt==0){
    			Cane c(name, age, gender, breed);
    			cout << "elemento creato:";
    			c.stampa();
    			r.tipo="Cane";
    			ptr=&c;
    		}else if(a_opt==1){
    			Gatto g(name, age, gender, breed);
    			cout << "elemento creato:";
    			g.stampa();
    			r.tipo="Gatto";
    			ptr=&g;
    		}else cout << "Opzione non valida, reinseriscila: ";
    	}while(a_opt!=0 && a_opt!=1);
    	crea_record(r, ptr);
    }
    La corretta implementazione è:

    codice:
    	do{
    		cin >> a_opt;
    		if(a_opt==0){
    			Cane c(name, age, gender, breed);
    			cout << "elemento creato:";
    			c.stampa();
    			r.tipo="Cane";
    			Animale* ptr=&c;
    			crea_record(r, ptr);
    		}else if(a_opt==1){
    			Gatto g(name, age, gender, breed);
    			cout << "elemento creato:";
    			g.stampa();
    			r.tipo="Gatto";
    			Animale* ptr=&g;
    			crea_record(r, ptr);
    		}else cout << "Opzione non valida, reinseriscila: ";
    	}while(a_opt!=0 && a_opt!=1);
    }

  5. #5
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Visto che mi hai anticipato, ti spiego il perché succede.
    Intanto...
    Non posso cambiare i parametri perchè devo seguire il testo della traccia.
    Che immagino imponga l'uso di char* al posto di std::string, giusto?

    In il problema si divide in due parti:
    la prima (che hai risolto) era un tuo errore, la seconda è un comportamento del C++ ai limiti della "undefined behaviour" (comportamento indefinito).
    codice:
    // in crea_animale()
    ...
    
    	do{
    		cin >> a_opt;
    		if(a_opt==0){
    //			Cane c(name, age, gender, breed); 
    			ptr = new Cane(name, age, gender, breed); 
    			cout << "elemento creato:";
    //			c.stampa(); 
                            ptr->stampa();
    			r.tipo="Cane";
    //			Cane* c_ptr=&c; 
    //			ptr=c_ptr; 
                           < * > 
                     }else if(a_opt==1){
    // eccetera
    Osserva lo scope di c nelle righe rosse. E' interno all'if. Questo vuol dire che nel punto indicato in blu, c, con annessi e connessi, non esisterà più. Questo lascia ptr in un limbo: da un lato sa a cosa puntare (ha la dichiarazione di una classe di tipo Animale), dall'altro l'oggetto a cui dovrebbe puntare non esiste più insieme alla sua vtable.
    Qui entra in gioco il comportamento "strano" (forse voluto visto che si parla di un linguaggio ad alta efficenza) del C++.

    E' possibile infatti (per quanto possa sembrare assurdo) richiamare una funzione membro, da un puntatore a una classe, senza avere l'istanza della classe! Purchè il puntatore stesso non sia nullo e la funzione membro non acceda a dati interni della classe. Detto in soldoni:
    codice:
    struct Foo {
    	void test() {
    		cout << "ciao" << endl;
    	}
    };
    
    int main(int argc, char* argv[] ) {
    
    	Foo* f;
    	f->test();
    }
    funziona! (Testato con VC++ 2010 in release mode, in debug lancia un giusto run time check error).

    Pertanto l'errore di "chiamata a funzione virtuale pura" è dovuto al fatto che stai usando un puntatore "sporco" di classe Animale, ma non avendo più nessun riferimento in vtable a una funzione get_razza() ridefinita in una classe derivata, il compilatore è costretto a chiamare quel che trova; vale a dire la funzione virtuale pura.

    Ci sono poi altri errorini: per esempio usare char* name="" (è un puntatore, sebbene ci si voglia illudere che sia una stringa C. E il valore di default per i puntatori è NULL e basta.
    Poi:
    codice:
    void stampa_dati(char* name1, const int n_record){
    	ifstream f;
    	Record vet[n_record];
    	f.open(name1, ios::in);
    (DEVC++ o derivati si sgama subito.)
    NON puoi dichiarare un array in quel modo, usando una variabile il cui valore è noto a run time.
    Il fatto che sia const non significa niente.
    Se ti serve un array di Record la cui dimensione puoi conoscerla solo a run time, allocala nell'heap con new.

    Ultima cosa:
    quando chiede la motivazione e inserisco una stringa, a me va in loop. ma non ho controllato a fondo il perché.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.