View Full Version : [c++] oggetti classe astratta
mistergks
08-09-2014, 16:30
Sto cercando di capire delle cose.
Ho una classe astratta (classe base) con metodi virtuali puri.
Poi ho una seconda classe (classe figlia.. Con metodi NON virtuali puri) che eredita la classe astratta.
So che non si possono istanziare oggetti di una classe astratta.
Le mie domande sono:
1) posso istanziare un puntatore a un oggetto?
2)posso istanziare un oggetto se definisco tutti i metodi virtuali puri della classe astratta?
AnonimoVeneziano
08-09-2014, 17:52
Sto cercando di capire delle cose.
Ho una classe astratta (classe base) con metodi virtuali puri.
Poi ho una seconda classe (classe figlia.. Con metodi NON virtuali puri) che eredita la classe astratta.
So che non si possono istanziare oggetti di una classe astratta.
Le mie domande sono:
1) posso istanziare un puntatore a un oggetto?
2)posso istanziare un oggetto se definisco tutti i metodi virtuali puri della classe astratta?
Sono tutte semplici domande che avresti potuto rispondere da solo facendo un esempietto da 10 linee :) Ti consiglio di farlo in futuro, ti salva un sacco di tempo che chiedere sul forum.
Detto questo:
1) Un puntatore e' sempre instanziabile, perche' e' uno dei tipi primitivi. Il poter instanziare puntatori a classi base astratte e' una delle caratteristiche fondamentali su cui si basa l' OOP in C++.
2) Ovviamente si :) Devi ereditare la classe base e definire i metodi virtuali che non sono stati implementati nella classe base. Se li definisci tutti la classe derivata non e' più astratta ed e' normalmente istanziabile.
Ciao
puoi usare dei nomi in maiuscolo per le classi e in minuscolo per i metodi/funzioni membri delle classi ?
Comunque come regola generale non puoi instanziare nessun tipo che sia incompleto ma puoi avere un puntatore ad un tipo incompleto .
Va da se che se non puoi instanziare un tipo incompleto, dereferenziare un puntatore ad un tipo incompleto non è una operazione permessa e anche se ci provi dovresti ottenere un errore in fase di compilazione .
Per quanto riguarda le funzioni/metodi virtual è una questione di design e convenienza relativa all'ereditarietà e a quanto ti conviene riusare il codice di 1 classe in più sottoclassi, è un modo per scrivere meno codice e/o ottenere un certo comportamento dai tipi .
Il punto è semplice, se non definisci tutte le funzioni che sono marcate come pure virtual nel tipo X non puoi istanziare quel tipo, e quel tipo ti serve solo come base dalla quale partire per costruire tutte le sotto-classi del tipo X, ovvero classi che ereditano da X .
Questo è qualcosa di molto simile rispetto a quanto accade in C con tipi struct incompleti, in C++ una classe astratta ( nota che in C++ non esistono classi astratte in sé per sé, come qualcosa di definito da una keyword, "classe astratta" è più un retaggio proveniente da Java, in C++ esistono tipi incompleti e metodi virtual/pure virtual, comunque tutti i programmatori C++ sanno cos'è una "classe astratta" in C++ ), alla fine si tratta sempre di tipi incompleti, per completare un tipo ti servono sempre 2 componenti ben definite: la parte relativa alle variabili di istanza e la parte relativa ai metodi/funzioni, i membri del tuo tipo, se queste 2 parti sono ben definite secondo il linguaggio il tuo compilatore saprà cosa farci, altrimenti puoi al massimo creare puntatori e/o riferimenti e il tuo tipo non avrà più valore di una semplice tag, di un semplice nome .
Sono tutte semplici domande che avresti potuto rispondere da solo facendo un esempietto da 10 linee :) Ti consiglio di farlo in futuro, ti salva un sacco di tempo che chiedere sul forum.
Detto questo:
1) Un puntatore e' sempre instanziabile, perche' e' uno dei tipi primitivi. Il poter instanziare puntatori a classi base astratte e' una delle caratteristiche fondamentali su cui si basa l' OOP in C++.
2) Ovviamente si :) Devi ereditare la classe base e definire i metodi virtuali che non sono stati implementati nella classe base. Se li definisci tutti la classe derivata non e' più astratta ed e' normalmente istanziabile.
Ciao
ma anche no.
la ragione per la quale si può instanziare un puntatore ad un tipo incompleto è che anche non disponendo della definizione del tipo, e quindi non sapendo il "layout" e la dimensione del tipo, la dimensione di un puntatore è definita dall'ambiente che usi, ergo è possibile costruire ed instanziare una variabile che sia espressione relativa a T*; dereferenziare quello a cui punta T* sarà un altro paio di maniche, ma intanto il puntatore T* lo puoi creare semplicemente perché non ti serve praticamente nessuna informazione su T per creare T* .
tu adesso potresti scrivere il codice che ti pare, quanti tipi incompleti ti pare e piace, ma se sei sotto un ambiente a 32 bit, con tutta probabilità il tuo T* t; sarà definibile come una variabile da 32 bit / 4 byte ( 1 byte = 8 bit ) o se sei sotto un ambiente 64 bit t sarà una variabile da ben 8 byte .
vendettaaaaa
09-09-2014, 00:07
ma anche no.
la ragione per la quale si può instanziare un puntatore ad un tipo incompleto è che anche non disponendo della definizione del tipo, e quindi non sapendo il "layout" e la dimensione del tipo, la dimensione di un puntatore è definita dall'ambiente che usi, ergo è possibile costruire ed instanziare una variabile che sia espressione relativa a T*; dereferenziare quello a cui punta T* sarà un altro paio di maniche, ma intanto il puntatore T* lo puoi creare semplicemente perché non ti serve praticamente nessuna informazione su T per creare T* .
tu adesso potresti scrivere il codice che ti pare, quanti tipi incompleti ti pare e piace, ma se sei sotto un ambiente a 32 bit, con tutta probabilità il tuo T* t; sarà definibile come una variabile da 32 bit / 4 byte ( 1 byte = 8 bit ) o se sei sotto un ambiente 64 bit t sarà una variabile da ben 8 byte .
Beh non mi pare che la risposta di Anonimo sia sbagliata... Il compilatore conosce i tipi primitivi, quelli user-defined dobbiamo "spiegarglieli" noi. Ergo un tipo primitivo non è mai incompleto per definizione. Ergo un tipo primitivo è sempre istanziabile...
Beh non mi pare che la risposta di Anonimo sia sbagliata... Il compilatore conosce i tipi primitivi, quelli user-defined dobbiamo "spiegarglieli" noi. Ergo un tipo primitivo non è mai incompleto per definizione. Ergo un tipo primitivo è sempre istanziabile...
sarà pure corretta ma mi sembra molto sui generis, è anche vero che potresti considerare un puntatore come un tipo a sé, ma poi devi spiegare e illustrare perché non puoi dereferenziare un puntatore anche se è possibile dichiararlo .
mistergks
09-09-2014, 12:31
2) Ovviamente si :) Devi ereditare la classe base e definire i metodi virtuali che non sono stati implementati nella classe base. Se li definisci tutti la classe derivata non e' più astratta ed e' normalmente istanziabile.
E se li definisco tutti i metodi virtuali puri della classe base all'interno della classe derivata.. Anche la classe base diventa una classe concreta o rimane astratta e quindi non istanziabile?
AnonimoVeneziano
09-09-2014, 15:18
E se li definisco tutti i metodi virtuali puri della classe base all'interno della classe derivata.. Anche la classe base diventa una classe concreta o rimane astratta e quindi non istanziabile?
La definizione della classe base e' del tutto indipendente dalla definizione delle classi derivate. La classe base rimane e rimarrà sempre astratta a meno che non la cambi tu direttamente dandogli delle implementazioni di default.
mistergks
09-09-2014, 16:02
E quindi non posso istanziare oggetti della classe base pur definendo tutti i metodi virtuali puri della classe base all'interno della classe derivata?
E quindi non posso istanziare oggetti della classe base pur definendo tutti i metodi virtuali puri della classe base all'interno della classe derivata?
Yes, l'ereditarietà di una classe astratta serve solo a te in qualità di programmatore, per quanto riguarda il linguaggio si tratta di un tipo incompleto .
mistergks
09-09-2014, 16:31
Nemmeno un puntatore alla classe base?
AnonimoVeneziano
09-09-2014, 17:10
Nemmeno un puntatore alla classe base?
Il puntatore di una classe puo' essere sempre creato e usato , anche per riferirsi alle classi derivate.
Per esempio se hai :
class A { // Classe astratta };
class B : public A { // Classe NON astratta };
Puoi fare:
A *ptr = new B();
ptr->metodo(); // Metodo deve essere definito in A (pure virtual o no)
Non potrai mai fare invece:
A *ptr = new A();
A obj;
o qualsiasi altro codice che instanzi direttamente A.
La classe "A" serve solo per definire una interfaccia da usare poi istanziando una
qualche classe derivata.
Ti consiglio di prenderti un libro e di leggerti/rileggerti il capitolo su ereditarieta' e polimorfismo, perche' sembra che tu non abbia capito a cosa serve l'esistenza delle classi astratte :)
Vedrai che dopo tutto prendera' immediatamente senso.
Ciao
mistergks
09-09-2014, 18:17
E posso fare questo?:
list<A>
E questo?
list<A *>
vendettaaaaa
09-09-2014, 18:28
E posso fare questo?:
list<A>
E questo?
list<A *>
No, sì.
mistergks
09-09-2014, 19:12
L'istanziazione qui invece è corretta?? Parlo dell'oggetto "Rifiuto"
class GestoreOrdineRifiuti : protected list
{
public:
GestoreOrdineRifiuti();
Rifiuto* pop();
unsigned size() const;
void push (Rifiuto* r)
{
list::iterator it = begin();
while ( it!= end() && *r < *(*it) )
it++;
insert (it, r );
}
};
void SistemaSmaltimento::smaltisciTutti( const GestoreOrdineRiifuti& gestore)
{
while (gestore.size() > 0 )
{
Rifiuto* r = gestore.pop();
r->smaltisci(this);
delete (r);
}
}
Svolgimento
progettare utilizzando l’ereditarietà e il polimorfismo, le interfacce ( solo file di intestazione ) della classe Rifiuto e di almeno due classi che modellano particolari riifuti
enum Tipo { Organico = 0 , Allumino, Plastica };
class Rifiuto
{
public:
Rifiuto ( Tipo, string, string );
virtual ~Rifiuto( );
virtual string getProvenienza( ) const = 0;
virtual string getDestinazione ( ) const = 0;
virtual Tipo getTipo( ) const = 0;
virtual void smaltisci(SistemaSmaltimento* ) const = 0;
protected:
Tipo tipo;
string provenienza;
string destinazione;
};
class Organico : public Rifiuto
{
protected:
int quantità;
string data;
public:
Organico( );
~Organico( );
void setQuantita( int );
void setData ( string );
virtual string getProvenienza( ) const { return provenienza };
virtual string getDestinazione ( ) const { return destinazione };
virtual Tipo getTipo ( ) const { return tipo };
virtual void smaltisci ( SistemaSmaltimento*);
};
//la classe Allmumino sarà uguale
implementare tutti i metodi lasciati incompleti nella classe GestoreOrdineRifiuti
//costruttore GestoreOrdineRifiuti( ), di default perché non ci sono campi da settare
GestoreOrdineRifiuti::GestoreOrdineRifiuti( ) {}
//funzione pop( )
Rifiuto* GestoreOrdineRifiuti::pop( )
{
Rifiuto rifiuto = back( );
pop_back( );
return rifiuto;
}
//funzione size( )
unsigned GestoreOrdineRifiuti::size( )
{
return list.size( );
}
mistergks
12-09-2014, 18:00
Up
tomminno
15-09-2014, 12:58
Se non sbaglio il tuo codice non istanzia mai un oggetto di un tipo derivato da Rifiuto, quindi è difficile risponderti :)
Il codice ha un po' di problemi: dichiari che GestoreOrdineRifiuti estende list
ma nel metodo size restituisci list.size(), il compilatore dovrebbe darti errore, chi è list?
Al metodo pop gli manca un puntatore. Da questo metodo mi pare di capire che list non sia std::list, ma una tua implementazione giusto? Altrimenti dovresti rivedere il design in quanto è male derivare dai container standard non avendo questi il distruttore virtuale.
Nota di colore:
r->smaltisci(this);
Il rifiuto smaltisce il sistema di smaltimento dei rifiuti? ;)
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.