Visualizzazione dei risultati da 1 a 9 su 9
  1. #1
    Utente di HTML.it
    Registrato dal
    Apr 2009
    Messaggi
    306
    Ciao ragazzi rispondo a questa discussione , poichè me ne ha tolti già molti.

    Una classe astratta può avere un costruttore di copia?ed una funzione di ridefinizione dell'operatore =?

    mi rispondo da solo di si, perchè se appunto avete già detto che può avere un dato allocato dinamicamente, e quindi della necessità di definire un distruttore. Però cosa cambierà da un costruttore di copia e ridefinizione dell'operatore =, rispetto ad una classe normale?

  2. #2
    Il costruttore di copia di una classe astratta fa quello che farebbe in una classe concreta... ma può essere richiamato solo ed esclusivamente dai costruttori di copia delle classi derivate, dato che una classe, se è astratta, non è istanziabile. Di fatto quindi non sarà altro che un helper per le classi derivate per copiare i membri della classe base.

    L'operatore di assegnamento è una bestia strana nel caso di classi astratte... anche in questo caso, si dovrebbe limitare a fare da helper per le classi derivate, dato che una copia "da classe astratta a classe astratta" non ha motivo di esistere. Per questo motivo al massimo lo dichiarerei come virtual e protected, in modo che sia utilizzabile come helper dalle classi derivate ma non direttamente "dall'esterno".

    Nota tra l'altro che, lavorando con gerarchie di classi di questo genere, spesso più che di un costruttore di copie/operatore di assegnamento serve piuttosto un metodo "clona" (vedi ad esempio qui).
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Utente di HTML.it
    Registrato dal
    Apr 2009
    Messaggi
    306
    Originariamente inviato da MItaly
    Il costruttore di copia di una classe astratta fa quello che farebbe in una classe concreta... ma può essere richiamato solo ed esclusivamente dai costruttori di copia delle classi derivate, dato che una classe, se è astratta, non è istanziabile. Di fatto quindi non sarà altro che un helper per le classi derivate per copiare i membri della classe base.

    L'operatore di assegnamento è una bestia strana nel caso di classi astratte... anche in questo caso, si dovrebbe limitare a fare da helper per le classi derivate, dato che una copia "da classe astratta a classe astratta" non ha motivo di esistere. Per questo motivo al massimo lo dichiarerei come virtual e protected, in modo che sia utilizzabile come helper dalle classi derivate ma non direttamente "dall'esterno".

    Nota tra l'altro che, lavorando con gerarchie di classi di questo genere, spesso più che di un costruttore di copie/operatore di assegnamento serve piuttosto un metodo "clona" (vedi ad esempio qui).
    Ciao Mitaly grazie per la risposta

    tieni presente che non sono un esperto e quindi con la parola helper (aiutante) non è che capisco cosa vuoi dire di preciso


    quindi il costruttore di copia dovrebbe essere definito come funzione virtuale pura così come pure l'operatore di assegnamento?



    stavo ragionando proprio su un problema con classe astratta e dati con estensione dinamica, se ti va, ed hai 5 minuti, mi potresti dire in base a quello che hai detto sul costruttore di copia e sull'operatore di assegnamento cosa dovrei fare di diverso?

    codice:
    #ifndef _COMPUTER_H_
    #define _COMPUTER_H_
    
    #include <iostream>
    #include <string.h>
    using namespace std;
    class Computer{
       private:
          char* modello;
          float prezzo;
          
       public:
          Computer():modello(0),prezzo(0){};
          Computer(char*, float) ;
          Computer(const Computer & )  ;
          virtual ~Computer(){delete [] modello;}
          
          virtual void stampaDati();
          virtual bool offerta(float &)=0;
          
          const Computer & operator=(const Computer & ) ;
    
    };
    
    #endif
    
    #include "Computer.h"
    
    
    Computer::Computer(char* st, float n){
       modello = new char [strlen(st)+1];
       strcpy(modello,st);
       prezzo=n;
    }   
       
    Computer::Computer(const Computer & c){
       modello = new char [strlen(c.modello)+1];
       strcpy(modello,c.modello);
       prezzo=c.prezzo;
    }   
    
    
    void Computer::stampaDati(){
       cout<<"il modello e' :   "<<modello<<"il prezzo e':  "<<prezzo<<endl;
    }   
    
    
    const Computer & Computer::operator=(const Computer & c){
       delete [] modello;
       modello = new char [strlen(c.modello)+1];
       strcpy(modello,c.modello);
       prezzo=c.prezzo;
       return *this;
    }
    
    #ifndef _NOTEBOOK_H_
    #define _NOTEBOOK_H_
    
    #include "Computer.h"
    #include <iostream>
    #include <string.h>
    using namespace std;
    
    class Notebook: public Computer{
       private:
          float peso;
          float spessore;
          float pollici;
       
       public:
          Notebook():Computer(),peso(0),spessore(0),pollici(0){};
          Notebook(char*, float, float, float, float) ;
          
          virtual void stampaDati() ;
          virtual bool offerta(float & ) ;
       };
          
    
    #endif
    
    #include "Notebook.h"
    
    Notebook::Notebook(char* st, float c, float n1, float n2, float n3):Computer(st,c){
       peso=n1;
       spessore=n2;
       pollici=n3;
    }   
       
    
    bool Notebook::offerta(float & of){
       if(peso >= 2 && pollici <= 14){
          of = prezzo-prezzo*20/100;
          return true;
       }   
       else return false;
    }
     
       
    void Notebook::stampaDati(){
       Computer::stampaDati() ;
       cout<<peso<<spessore<<pollici<<endl;
    }
    
    #ifndef _PDA_H_
    #define _PDA_H_
    
    #include "Computer.h"
    #include <iostream>
    #include <string.h>
    using namespace std;
    
    class Pda: public Computer{
       private:
          char* sisop;
          bool bluetooth;
       
       public:
          Pda():Computer(),sisop(0),bluetooth(0){};
          Pda(char*, float, char*, bool );
          Pda(const Pda &);
          ~Pda(){ delete [] sisop;}
          
          virtual bool offerta(float & ) ;
          virtual void stampaDati();
       };
          
    
    #endif
    
    #include "Pda.h"
    
    
    Pda::Pda(char* sr , float c, char* psr, bool pb):Computer(sr,c){
       sisop = new char [strlen(psr)+1];
       bluetooth=pb;
    }   
    
    Pda::Pda(const Pda & p):Computer(p){
       sisop = new char [strlen(p.sisop)+1];
       bluetooth=p.bluetooth;
    }
    
    
    bool Pda::offerta(float & of){
       if(strcmp(getmodello(),"nokia") && bluetooth){
          of = prezzo-prezzo*15/100;
          return true;
       }   
       else return false;
    }
     
       
    void Pda::stampaDati() {
       Computer::stampaDati();
       cout<<bluetooth<<sisop<<endl; 
    }

  4. #4
    Originariamente inviato da Alfoxx
    Ciao Mitaly grazie per la risposta

    tieni presente che non sono un esperto e quindi con la parola helper (aiutante) non è che capisco cosa vuoi dire di preciso
    Intendo dire che risultano metodi "di aiuto" per quelli implementati nelle classi derivate, dato che non ha senso (né è possibile, nel caso del costruttore di copie) invocarli per i fatti loro. Già nel tuo codice stai usando il costruttore di copie della classe base nella maniera che intendevo (lo richiami come "aiuto" per costruire la nuova istanza della classe derivata, mai direttamente - anche perché non è possibile).
    quindi il costruttore di copia dovrebbe essere definito come funzione virtuale pura così come pure l'operatore di assegnamento?
    No. Può essere utile implementare il costruttore di copia e l'operatore di assegnamento (se ci sono dei campi da copiare nella classe base non ha senso reimplementare la logica di copia in tutte le classi derivate), ma questa logica di copia può essere richiamata solo dai corrispondenti metodi delle classi derivate e non direttamente sulla classe base, dato che:
    • un costruttore di copia è pur sempre un costruttore (=costruisce un nuovo oggetto), e non è possibile istanziare direttamente una classe astratta;
    • in teoria è possibile definire un operatore di assegnamento pubblico non virtuale nella classe base, ma non è una buona idea, dato che questo copierebbe solo i campi della classe base, ignorando gli eventuali campi aggiunti dalle classi derivate. È fondamentale quindi renderlo virtuale; può essere anche un'idea renderlo virtuale puro ma fornirne un'implementazione - fornisci un'implementazione di base che può essere usata come "helper" dalle classi derivate, ma imponi che il metodo venga ridefinito, in modo da assicurarsi che non venga lasciata la versione della classe base "per sbaglio".

    stavo ragionando proprio su un problema con classe astratta e dati con estensione dinamica, se ti va, ed hai 5 minuti, mi potresti dire in base a quello che hai detto sul costruttore di copia e sull'operatore di assegnamento cosa dovrei fare di diverso?
    Il costruttore di copie va bene così, l'operatore di assegnamento andrà definito come virtuale.

    Ma l'errore grosso qui è usare i char *: la tua classe dovrebbe occuparsi di rappresentare un oggetto-computer, non stare a perdere tempo (e scrivere codice duplicato) per la gestione delle stringhe - in violazione del SRP. Della gestione delle stringhe si deve occupare un'altra classe - tipicamente si usa std::string - che si occupa di come gestire la memoria, fare copie, gestire l'allocazione & co.
    Una volta che il problema-stringa è risolto dalla classe std::string tu puoi semplicemente dichiarare i tuoi campi di tipo std::string e dimenticarti del problema dell'allocazione/deallocazione delle stringhe, e quindi di costruttori di copia, distruttori e operatori di assegnamento: quelli generati automaticamente dal compilatore richiamano i corrispondenti metodi sui campi della tua classe, che è tutto quello di cui tu hai bisogno nel momento in cui usi classi che incapsulano correttamente le risorse a cui ti riferisci.

    Per questo secondo me la questione dei costruttori di copia e degli operatori di assegnamento in genere è sopravvalutata e spesso viene gestita in maniera inutilmente complicata: la cosa fondamentale è scrivere delle classi che gestiscano perfettamente le "risorse critiche" che necessitano di essere allocate/deallocate/copiate/... (memoria, file aperti, socket, ...), una volta che queste funzionano bene negli oggetti che le utilizzano non è necessario fare alcunché di particolare: i "big three" (operatore di assegnamento, distruttore e costruttore di copie) generati dal compilatore a questo punto andranno più che bene. Da tanti esempi che vedo in giro sembra che ogni classe che contiene una stringa si debba stare a scrivere la sua logica di costruzione/distruzione/copia delle stringhe: sciocchezze; si risolve il problema una volta per tutte in una classe a parte apposta e non ci si pensa più.

    ---

    In altre parole: in genere ha senso definire i "big three" solo per classi che gestiscono direttamente delle risorse, e in tal caso si deve trattare di classi che fanno quello e nient'altro. Per questo motivo la loro realizzazione viene molto semplificata (una classe di gestione di una risorsa difficilmente avrà situazioni "strane" di ereditarietà, metodi virtuali & co., e deve "pensare" ad una sola cosa), e analogamente la realizzazione di oggetti che fanno uso di tali classi risulta molto più semplice (un oggetto Computer si occuperà solo degli aspetti relativi a quello che deve rappresentare, non a minuzie di gestione delle stringhe).

    ---


    Tra parentesi, ho provveduto a dividere questi post dalla discussione precedente, dato che ci stiamo incentrando su aspetti leggermente diversi del problema (e risollevare discussioni arcaiche non è pratica comune in questo forum).
    Amaro C++, il gusto pieno dell'undefined behavior.

  5. #5
    Utente di HTML.it
    Registrato dal
    Apr 2009
    Messaggi
    306
    Grazie mille Mitaly

    la tua spiegazione è davvero articolata, grazie mille, domani a mente fresca la rivedrò e annoterò qualcosa sicuramente, però ti devo dire che forse essendo tu ad un grado molto superiore di conoscenza del c++, sai anche le "agevolazioni", invece io sto proprio alla base e sto cercando di far quadrare tutto. Anche perciò ho scritto sotto l'altra discussione, perchè sulle classi astratte mi ha tolto davvero tanti dubbi che avevo.

    Un mesetto fa credevo che i costruttori di copia, e l'operatore di assegnamento ed i distruttori fossero a piacere, se metterli o no, invece poi sono andato dalla prof, e lei mi ha detto " le classi si devono fare bene, con tutto ciò che serve, mia per farmi piacere ?".


    Per me la programmazione è spiegata male, o forse sono i libri di testo che i miei prof adottano, ma non a vedere come al solito il quadro di insieme, come invece hai mostrato tu con la tua risposta, e sto cercando lentamente di farmelo anche io.

  6. #6
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Probabilmente il vero problema è che si insegna il C++ partendo dal C. Come dire insegnare l'italiano partendo dal latino. Di fatto ormai sono linguaggi diversi e la retrocompatibilità è più una questione di comodo che tecnica.
    Con questo non voglio dire che il C sia obsoleto, tutt'altro: solo che la parte C del C++ andrebbe usata (come suggerisce Stroustrop) solo per interfacciarsi con codice legacy (vecchio e non modificabile), oppure se si è compreso abbastanza bene le vere problematiche del C++
    Per come la vedo io la prima cosa che andrebbe insegnata in un corso di C++ è la classe std::string per gestire le stringhe.

    Purtroppo spesso il linguaggio si evolve più rapidamente dei professori e restare al passo non è per niente facile.
    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.

  7. #7
    Utente di HTML.it
    Registrato dal
    Apr 2009
    Messaggi
    306
    il C non l'abbiamo mai trattato, cmq si hai ragione, ed anche la scelta dei libri e l'impostazione delle proprie lezioni è di difficile modernizzazione, me ne rendo conto, però il rischio è appunto non permettere di avere lo sguardo d'insieme, cosa che i prof non si rendono conto, almeno credo io.

  8. #8
    Secondariamente, secondo me il C++ non è adatto come primo linguaggio da imparare.

    Ai concetti astratti fondamentali da imparare (OOP, template, aspetti di programmazione funzionale nella STL, ...) si vanno ad aggiungere troppe "assurdità implementative", corner cases e "leaky abstractions" specifiche del C++ (vai a spiegare ad un newbie perché, se str è una std::string, str+"asd" va bene, "asd"+str va bene ma "asd"+"def" non va bene), un modello di compilazione/linking non immediato da comprendere (file di inclusione, translation units, ODR, librerie statiche, ...), il dualismo dichiarazione/definizione, perché nelle classi si separano .hpp/.cpp (...ma per i template no), una libreria standard che è un po' procedurale (C), un po' OOP e un po' "generic programming", oltre a tante altre complicazioni; ci si ritrova insomma a dover lottare, prima ancora che con i concetti "generali" della programmazione, con ostacoli puramente "tecnici" (e non concettuali) che altri linguaggi non hanno.

    In tutto questo, quando viene spiegato il linguaggio non viene mai spiegata chiaramente la distinzione tra i tre piani, che sono quello del concetto astratto (cos'è una classe in generale), la sua implementazione specifica nel C++ "secondo lo standard" (la sintassi è questa e questa, si può fare queste cose, queste altre è undefined behavior), e cosa fa effettivamente il codice compilato (da cui si possono capire meglio gli errori in cui ci si imbatte, nonché si comprende il perché di certe scelte nel progettare il linguaggio, cosa conviene dal punto di vista delle performance, e come non ci sia nessuna magia dietro ai costrutti del linguaggio).

    A questo si aggiunge che secondo me spesso è la OOP stessa ad essere insegnata male. Si fanno esempi che vorrebbero essere ispirati alla vita reale ma che si prestano male a spiegare quale può essere l'utilità effettiva di una classe; ho visto professori scrivere classi di cui non sono nemmeno definite chiaramente le responsabilità (modello everything and the kitchen sink, con metà dei metodi che funzionano solo in casi ad-hoc), confondere relazioni "is-a" con "has-a" (risultato: ereditarietà usata a caso), confondere le idee alla gente sulla questione public/private (per cui spesso passa il concetto che sia per "proteggere i dati" invece che per esplicitare quale sia l'interfaccia e quale il dettaglio implementativo), ... che tristess...
    Amaro C++, il gusto pieno dell'undefined behavior.

  9. #9
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Originariamente inviato da MItaly
    Secondariamente, secondo me il C++ non è adatto come primo linguaggio da imparare.
    Concordo in pieno. E' come fare scuola guida con una F1.
    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.