DOMANDA c++ classi: costruttore e distruttore

Peri

Nuovo Utente
82
3
Salve
Domanda magari un po' scema, ma non ne esco.
Da un po' mi sto cimentando nella programmazione c++ e spesso sbatto il muso su certi errori del compilatore che mi mandano al manicomio.
Riguarda il costruttore, il distruttore, const e &
C++:
#include <cstdlib.h>

class indirizzo
{
private:
    void* ptr;
    size_t size;
public:
    indirizzo(size_t s)
    {
        size = s;
        ptr = malloc(s);
    }
    ~indirizzo()
    {
        free(ptr);
    }
}
Per dare l'idea di quello che voglio spiegare, mettiamo caso che abbia una classe chiamata "indirizzo"
Mia premura è gestire consapevolemte la memoria e capire bene quando si attiva il costruttore e quando si attiva il distruttore
Mettiamo che io abbia queste funzioni:
C++:
void func_1( indirizzo p )
{
    /*...*/
}

void func_2( const indirizzo p )
{
    /*...*/
}

void func_3( indirizzo & p )
{
    /*...*/
}

void func_4( const indirizzo & p )
{
    /*...*/
}

Oppure supponiamo che abbia una funzione nella quale creo un oggetto indirizzo che viene poi ritornato
C++:
indirizzo func()
{
    /*...*/
}

class //...
{
    /*...*/
    indirizzo func() const
    {
        /*...*/
    }
}

//nella classe indirizzo
class indirizzo
{
    /*...*/
    indirizzo& func()
    {
        return *this;
    }
}

In questi casi per esempio non saprei garantire che la memoria non si perda (o che sia stata deallocata per sbaglio o che sia stata allocata per sbaglio) e non saprei garantire che non vi siano errori da parte del compilatore.
Capite bene che non va bene ahahahahahah
Sapete illuminarmi riguardo il comportamento del costruttore e del distruttori (o anche di altre cose) in questi casi?
 

Andretti60

Utente Èlite
6,440
5,091
Uno dei tanti motivi per cui molti a amanti del linguaggio C non piace proprio il C++, ha l’aggiunta delle classi ma non aiuta per nulla con i classici problemi del rilascio delle risorse allocate.

Per rispondere alla tua domanda, il costruttore è chiamato quando una instanza della classe è dichiarata, il distruttore è chiamato o quando la classe viene distrutta mediante un “delete” o quando la classe finisce il suo ciclo perché esce dal suo “scope” (non so come si dica in italiano). Questo a grandi linee, ci sono ovviamente dettagli.

Il grosso problema, come hai già capito, è quando la istanza della classe viene passata ad altri oggetti, c’è il pericolo di usare il “delete” quando la classe è ancora in uso. Ci sono vari metodi per aggirare tale pericolo (come per esempio l’uso di un contatore di referenza e ovviamente il garbage collector), ma rimane pur sempre un pericolo.

E ripeto, vale per ogni risorsa creata che deve poi essere chiusa, tipo apertura di file o socket (chiamati generalmente “handle”) o risorse grafiche.
 
  • Mi piace
Reazioni: Peri

bigendian

Utente Attivo
718
410
OS
Linux
intanto <cstdlib.h> dovrebbe essere <cstdlib> oppure <stdlib.h>

poi,

- utilizzando un "reference" come parametro funzione non viene generata una copia quindi non viene eseguiro alcun costruttore
- utilizzando l'oggetto stesso come parametro, ne crei una copia, il copy-constructor viene chiamato

Il distruttore viene chiamato ogni qualvolta un oggetto raggiunge la fine dello "scope" del codice, ovvero, termina la sua vita alla fine di un blocco di codice {}

Implementa una classe con costruttore, distruttore e "copy-constructor" (studia cos'e'). Metti dei printf o cout dentro le tre funzioni e capirai bene quello che accade.
 
  • Mi piace
Reazioni: Peri

Peri

Nuovo Utente
82
3
intanto <cstdlib.h> dovrebbe essere <cstdlib> oppure <stdlib.h>

poi,

- utilizzando un "reference" come parametro funzione non viene generata una copia quindi non viene eseguiro alcun costruttore
- utilizzando l'oggetto stesso come parametro, ne crei una copia, il copy-constructor viene chiamato

Il distruttore viene chiamato ogni qualvolta un oggetto raggiunge la fine dello "scope" del codice, ovvero, termina la sua vita alla fine di un blocco di codice {}

Implementa una classe con costruttore, distruttore e "copy-constructor" (studia cos'e'). Metti dei printf o cout dentro le tre funzioni e capirai bene quello che accade.
azz giusto, l'idea del cout non ci avevo pensato, farò dei giochini per capire meglio le dinamiche!
Post unito automaticamente:

Uno dei tanti motivi per cui molti a amanti del linguaggio C non piace proprio il C++, ha l’aggiunta delle classi ma non aiuta per nulla con i classici problemi del rilascio delle risorse allocate.

Per rispondere alla tua domanda, il costruttore è chiamato quando una instanza della classe è dichiarata, il distruttore è chiamato o quando la classe viene distrutta mediante un “delete” o quando la classe finisce il suo ciclo perché esce dal suo “scope” (non so come si dica in italiano). Questo a grandi linee, ci sono ovviamente dettagli.

Il grosso problema, come hai già capito, è quando la istanza della classe viene passata ad altri oggetti, c’è il pericolo di usare il “delete” quando la classe è ancora in uso. Ci sono vari metodi per aggirare tale pericolo (come per esempio l’uso di un contatore di referenza e ovviamente il garbage collector), ma rimane pur sempre un pericolo.

E ripeto, vale per ogni risorsa creata che deve poi essere chiusa, tipo apertura di file o socket (chiamati generalmente “handle”) o risorse grafiche.
Non mi occupo di programmazione quindi alcuni termini l'ho sentiti ma mai approfonditi farò delle ricerche!
Facendo simulazioni in ricerca operativa ho dovuto fare un bel level up dal C al C++. Inizialmente era una roba tranquilla (anzi che bene o male ho capito le potenzialità dell' OOP), ma poi come dici te è stato un po' complicato digerirla ?
 
Ultima modifica:

Peri

Nuovo Utente
82
3
approssimativamente "campo d'azione", la porzione di codice in cui una variabile è visibile ed utilizzabile
Nei manuali trovi scritti termini come "ambito", "visibilità" e "durata" che nella loro semplicità puoi farne uso per descrivere fenomeni relativi allo scope di una variabile. Solitamente "ambito" è la traduzione più corretta (da dizionario proprio).
 
  • Mi piace
Reazioni: Andretti60

Andretti60

Utente Èlite
6,440
5,091
Grazie, mi piace “ambito” (quando io studiai i testi erano solo in inglese), si era agli albori.
Il problema principale è chi è responsabile di creare un oggetto, e chi di distruggerlo. Se non è lo stesso oggetto, c’è un errore di progettazione. Ogni classe deve essere responsabile di creare le risorse di cui ha bisogno, e di distruggerle quando è necessario, almeno nel suo distruttore. “Passare” oggetti (o indirizzi) di tale risorse, è un grosso rischio e conduce al classico “spaghetti code”, ossia codice così intricato che non si capisce dove inizia e dove finisce. Il grosso vantaggio delle classi è quello di potere nascondere i propri dettagli dichiarando i membri privati, e mai esporli al mondo esterno se non come “proprietà” della classe stessa, ma solo la classe (ed eventualmente le classi derivate) li possano manipolare.
Un semplice esempio. Supponiamo che una classe sia responsabile di leggere dati da un file. La classe è responsabile di aprire il file, leggere dati a richiesta, chiudere il file. Il cliente NON deve nemmeno sapere che la classe abbia aperto un file. I dati possono venire da un file, ma anche da un socket, da un database, creati internamente o via dicendo. Il cliente usa un metodo della classe (GiveMeNextData) che ritorna un nuovo record dei dati (può essere un oggetto qualunque, di qualsiasi tipo), che la classe passi al cliente il puntatore del file è un grosso errore concettuale, in quanto il cliente potrebbe quindi modificarlo, addirittura chiuderlo. È una tecnica da evitare ad ogni costo.
Per questo motivo (e altri) la maggior parte del tempo passato in un progetto deve essere dedicato a progettare le classi di cui si ha bisogno, facendo modo che siano unità indipendenti, non abbiano conoscenza delle altre e che siano riutilizzabili. In pratica è come progettare lo schema di un database. Se la classe A usa la classe B, la classe B non deve conoscere nulla della classe A. Di fatto, la classe A non ha neanche bisogno di conoscere la classe B, ma solo una delle “interface” che la classe B implementa.

Se trovi tutto questo complicato e astruso, è perché sono concetti difficili. Non pretendere che tu li possa capire in un semplice commento di un forum, non ho nemmeno l’ardire di poterlo spiegare in pochi paragrafi. Il bisogno di avere solide fondamenta su tecniche di progetto sono nate fin dagli anni settanta, furono poi collezionate in un manuale che divenne poi la Bibbia della progettazione del software, i quattro autori presero poi il nomignolo di “Gang of Four”

E questo è solo la cima del iceberg. Non finisce qui. Conoscere le tecniche di progettazione non porta necessariamente a scrivere “buon codice”, il famoso Robert Martin (denominato “Uncle Bob”) ha scritto tutta una serie di manuali sull’argomento “clean code” incluso come applicarlo nello sviluppo della programmazione “agile”

Ti ho linkato solo pagine wiki che sono a livello divulgativo. Tutti questi concetti fanno parte di molteplici corsi universitari. Diventare ingegneri del software è un cammino lungo, che diventa sempre più difficile con il tempo. Quando mi laureai io, si leggeva il manuale di K&R sul linguaggio C e si cominciava a scrivere codice, realizzando poi quanto fosse facile creare un programma che funzionasse ma il cui codice faceva letteralmente schifo.

Scusa la lunghezza. Questi sono argomenti di cui mi piace parlare (avrai capito che lo faccio come lavoro, e sono ormai vicino alla pensione)
 
  • Mi piace
Reazioni: Peri e M1n021

finmat92

Utente Attivo
195
192
CPU
RYZEN 5 5600X
Dissipatore
SCYTHE FUMA 2
Scheda Madre
MSI B550 TOMAHAWK
HDD
1 TB 7200RPM + 1 TB XPG SX8200PRO
RAM
CRUCIAL BALLSTRIX DDR4 3600 MHZ 2X8 GB
GPU
MSI NVIDIA GAMING Z TRIO RTX 3070
Audio
INTEGRATO
Monitor
SAMSUNG T23C350
PSU
CORSAIR TX750M
Case
FRACTAL MESHIFY S2
Periferiche
CORSAIR K68
Net
TIM FTTC 200 Mbps
OS
WINDOWS 10 PRO
Uno dei tanti motivi per cui molti a amanti del linguaggio C non piace proprio il C++, ha l’aggiunta delle classi ma non aiuta per nulla con i classici problemi del rilascio delle risorse allocate.

Per rispondere alla tua domanda, il costruttore è chiamato quando una instanza della classe è dichiarata, il distruttore è chiamato o quando la classe viene distrutta mediante un “delete” o quando la classe finisce il suo ciclo perché esce dal suo “scope” (non so come si dica in italiano). Questo a grandi linee, ci sono ovviamente dettagli.

Il grosso problema, come hai già capito, è quando la istanza della classe viene passata ad altri oggetti, c’è il pericolo di usare il “delete” quando la classe è ancora in uso. Ci sono vari metodi per aggirare tale pericolo (come per esempio l’uso di un contatore di referenza e ovviamente il garbage collector), ma rimane pur sempre un pericolo.

E ripeto, vale per ogni risorsa creata che deve poi essere chiusa, tipo apertura di file o socket (chiamati generalmente “handle”) o risorse grafiche.
Una volta un saggio disse "In C è facile spararti ad un piede, il C++ lo rende più difficile ma quando lo fai salta l'intera gamba".
Post unito automaticamente:

Ti ho linkato solo pagine wiki che sono a livello divulgativo. Tutti questi concetti fanno parte di molteplici corsi universitari. Diventare ingegneri del software è un cammino lungo, che diventa sempre più difficile con il tempo. Quando mi laureai io, si leggeva il manuale di K&R sul linguaggio C e si cominciava a scrivere codice, realizzando poi quanto fosse facile creare un programma che funzionasse ma il cui codice faceva letteralmente schifo.

Un codice pieno di goto per esempio.....aaaaaah
 

Ci sono discussioni simili a riguardo, dai un'occhiata!

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!

Discussioni Simili