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).