Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Buongiorno. Se vi fosse possibile, avrei bisogno una conferma o un "indirizzo" in caso non ci fosse gran che da confermare.
Per un programmino che ho in mente, dovrei implementare un metodo per caricare da un file di testo una "catena" di comandi che intendo identificare tramite etichette testuali (vorrei che il file fosse leggibile anche "a occhio nudo") e serie di dati correlati al comando sulla stessa riga. Ciascun comando dovrebbe dar luogo a comportamenti in qualche modo imparentati tra loro, per cui pensavo di creare una classe COMANDO con gli aspetti generici, dalla quale derivare i diversi tipi con le caratteristiche più specifiche. C++, dunque, ed ereditarietà (e già questo è un terreno instabile, per me). Corretto?
Però, e qui viene il bello, per evitare di impazzire vorrei che tutti i comandi fossero "caricati" in un unico array di oggetti dal quale poter attingere liberamente in avanti, all'indietro e, se necessario, anche da posizioni "qualsiasi", durante l'esecuzione in risposta all'input dell'utente. Orbene, tempo addietro lessi in merito al polimorfismo, in realtà senza capirci gran che, ed ora mi piacerebbe provare a fare qualcosa che lo utilizzi perché MI SEMBRA possa darmi quel che mi serve.
Il ragionamento che faccio è: creo un array di puntatori ad oggetti di classe base COMANDO, tutti NULL, quindi lo "popolo" creando dinamicamente oggetti delle varie classi derivate ricavandone il tipo dal file che contiene le etichette (e altri dati).
A quel punto, "estraendo" un puntatore qualsiasi dall'array non dovrei aver bisogno di preoccuparmi di quale tipo di comando sia, bensì potrei chiamare un metodo (virtuale?) Esegui() che sarebbe implementato, con le varianti del caso, nelle singole classi che definiscono i diversi comandi... spero d'essere riuscito a spiegarmi...
Può funzionare? Sono fuori strada? Il polimorfismo non c'entra nulla?
Per il momento, prima di cimentarmi con l'impresa in grande stile (per le mie possibilità), sto facendo un programma di prova molto scarnificato che SEMBRA fare quel che ho tentato di descrivere. La classe base, in questo caso, si chiama BASE e le classi derivate DER1, DER2 e DER3. Il metodo generico non è Esegui(), bensì Mostra() e non fa cose molto utili -- si limita a evidenziare in quale classe alberga il corpo della funzione chiamata. Anche seguendo il flusso dell'esecuzione tramite un debugger sembra sia tutto in ordine, ma preferisco chiedere a chi ne sa più di me, per evitare di partire col piede sbagliato.
Il codice è su due file.
main.cpp
Codice sorgente - presumibilmente C++
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "polimorfico.h"
constchar kStrNomeFile[]="tipi.txt";
constchar kStrErrFileNonAperto[]="file non aperto\n";
BASE **distruggi_oggetti( BASE **ptrs, int qPtrs ){
if(NULL!=ptrs ){
for(int i=qPtrs-1; i>=0;--i ){
if(NULL!= ptrs[i]){
delete ptrs[i];
ptrs[i]=NULL;
}
}
delete[] ptrs;
ptrs =NULL;
}
return ptrs;
}
polimorfico.h
Codice sorgente - presumibilmente C++
#ifndef POLIMORFICO_H
#define POLIMORFICO_H
#include <cstdio>
enum{
tipo_non_valido =-1,
tipo_base,
tipo_der1,
tipo_der2,
tipo_der3,
quantita_tipi
};
constchar kStrNomiTipi[quantita_tipi][5]={
"base",
"der1",
"der2",
"der3"
};
class BASE {
public:
BASE(){ tipo = tipo_base;}
virtual ~BASE(){;}
void Tipo(int t ){ tipo = t>=0&&t<quantita_tipi?t:tipo_base;}
int Tipo(void){return tipo;}
virtualvoid Mostra(void)const
{printf("dalla classe base: tipo %s\n", kStrNomiTipi[tipo]);}
staticint TipoDaStringa(constchar*s );
protected:
private:
int tipo;
};
int BASE::TipoDaStringa(constchar*s ){
for(int i=tipo_base; i<quantita_tipi;++i )
if( 0 ==strcmp(s,kStrNomiTipi[i]))return i;
return tipo_non_valido;
}
class DER1 :public BASE {
public:
DER1(){ Tipo( tipo_der1 );}
virtual ~DER1(){;}
void Mostra(void)const{
puts("dalla classe derivata 1");
BASE::Mostra();
}
protected:
private:
};
class DER2 :public BASE {
public:
DER2(){ Tipo( tipo_der2 );}
virtual ~DER2(){;}
void Mostra(void)const{
puts("dalla classe derivata 2");
BASE::Mostra();
}
protected:
private:
};
class DER3 :public BASE {
public:
DER3(){ Tipo( tipo_der3 );}
virtual ~DER3(){;}
void Mostra(void)const{
puts("dalla classe derivata 3");
BASE::Mostra();
}
protected:
private:
};
#endif // POLIMORFICO_H
Il file dei dati, per le prove, per ora solo con le etichette testuali che identificano i tipi. Nel caso reale conto di usare un csv con una struttura più articolata.
tipi.txt
Codice sorgente - presumibilmente C/C++
base
base
der1
der1
fake
der3
der1
der2
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
Crei una classe Comando che ha un metodo virtuale puro esegui() e inserisci gli elementi in std::vector<unique_ptr<Comando>> inutile reinventare un array dinamico di puntatori!
Ho cercato il significato di "virtuale puro", ed ho scoperto che fa riferimento a metodi "= 0;" che rendono impossibile creare oggetti della classe che li comprende (il che ha perfettamente senso, in effetti: chiamando il metodo in questione, se ho ben capito, finiresti per chiamare un puntatore nullo). Io invece avrei bisogno un sistema "misto" nel quale una parte dei compiti sono comuni a tutti gli oggetti delle classi derivate e possono essere svolti dal metodo nella classe base, mentre solo alcuni dettagli o compiti accessori sarebbero diversi per ogni classe derivata. In un contesto simile, se ho ben interpretato quel che ho letto, un metodo "puro" renderebbe impraticabile questo tipo di caratteristica. Però, come ho detto, su quest'argomento non ho ALCUNA certezza (se no non chiederei). Che dici, son fuori strada?
Conosco poco o niente std, per cui usarlo mi renderebbe ancor più "estraneo" l'ambiente nel quale muovermi col rischio di portarmi a perdere senza più riuscire a capire da che parte arrivano gli eventuali errori. Non sono abbastanza "elastico" (ed esperto) per affrontare troppe novità in una sola volta. E' un difetto del quale sono fortunatamente consapevole. Ora vorrei capire l'altra questione, senza "interferenze".
Ultima modifica effettuata da AldoBaldo il 11/12/2018 alle 20:28
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
Considera una classe Comando con un esegui() virtuale puro... Crei una classe CopiaFile che eredita da Comando e definisci esegui() in CopiaFile, e che ha un metodo controlla() che non è dichiarato in Comando, quando tu chiami quel metodo su un oggetto la vtable viene usata per decidere che va chiamata la funzione esegui di CopiaFile, che farà effettivamente la copia di un file.
Se avrai bisogno di chiamare un metodo definito in una sola classe della tua gerarchia (ad esempio controlla()) utilizzerai auto cp = dynamic_cast<CopiaFile*>(cmd); con cmd che è una istanza di Comando. Se il risultato del dynamic_cast è non-nullo l'oggetto cmd è effettivente una istanza di CopiaFile e a quel punto è sicuro fare cp->controlla()
Postato originariamente da AldoBaldo: Ho cercato il significato di "virtuale puro", ed ho scoperto che fa riferimento a metodi "= 0;" che rendono impossibile creare oggetti della classe che li comprende (il che ha perfettamente senso, in effetti: chiamando il metodo in questione, se ho ben capito, finiresti per chiamare un puntatore nullo). Io invece avrei bisogno un sistema "misto" nel quale una parte dei compiti sono comuni a tutti gli oggetti delle classi derivate e possono essere svolti dal metodo nella classe base, mentre solo alcuni dettagli o compiti accessori sarebbero diversi per ogni classe derivata. In un contesto simile, se ho ben interpretato quel che ho letto, un metodo "puro" renderebbe impraticabile questo tipo di caratteristica. Però, come ho detto, su quest'argomento non ho ALCUNA certezza (se no non chiederei). Che dici, son fuori strada?
Non credo di aver avet capito cosa intendi....
Un metodo virtuale puro sta ad jndicare che il metodo è presente in tutte le sottoclassi della gerarchia, una classe è istanziabile solo se fornisce una implementazione dello stesso, e quindi chiamando quel metodo eseguirai la funzione che ti aspetti, perché la valutazione viene eseguita a runtime, e non dal compilatore.
Prova a vedere le classi astratte (utilizzano i metodi virtuali puri)
In pratica crei una classe di "base" , che , è vero che non puoi istanziare, ma puoi ereditarla nelle altre classi che userai.
In essa scrivi solo i metodi comuni. Nelle altre classi erediti la classe "base", ed scrivi i relativi metodi differenti.
Al limite puoi fare un "override" del metodo . Cioè cambi il funzionamento del metodo della classe astratta.
In c# funziona, e a quanto so la cosa è fattibile (con leggere differenze credo)
Vi ringrazio di cuore per le risposte, però devo confessare che le ho lette con fatica riuscendo a comprendere solo una minima parte di quella di Mikelius (TheDarkJuster, ho letto e riletto, ma non ci arrivo). Evidentemente è un passo troppo lungo per le mie gambe, oppure è uno di quei casi nei quali è più semplice fare che spiegare... Proverò a sondare ancora un po' il terreno con i miei test, poi deciderò se procedere con l'idea di usare queste tecniche o di lasciar perdere del tutto il progetto (almeno in questa forma). Che stoccata, però!
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
cmd->esegui();// Qui viene eseguita la funzione esegui() a seconda del tipo "dinamico" dell'elemento corrente, NON esegui() di Comando, anche se non fosse stata puramente virtuale
}
Ultima modifica effettuata da TheDarkJuster il 13/12/2018 alle 15:58
TheDarkJuster, ti meriti di nuovo un ringraziamento bello grosso, però sono troppe le nuove informazioni, troppi i costrutti e i concetti che mi proponi in una volta, non riesco a seguirti. E' un'impresa disperata.
P.S. Il libro sul C++ ce l'ho, anzi, ne ho tre (due cartacei, uno in formato elettronico) e li consulto regolarmente, ma risalgono agli anni '90 e su nessuno di essi compaiono alcune delle "formule" che ci sono nel tuo codice.
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.