Problemi col distruttore di una classe

di il
4 risposte

Problemi col distruttore di una classe

Buongiorno a tutti,
sono alle prese con un problema riguardo ad un distruttore. Ho creato una mia classe Vector (tutto questo è un esercizio per capire come creare una classe completa, so che esiste un vector standard) e ho creato qualche funzione base. Seguendo le indicazione dal libro da cui sto studiando (C++ Stroustrup) ho creato dei costruttori e degli assegnatori di copia e di spostamento per supportare alcune operazioni sui vettori che debbano usare uno o l'altro metodo.
Il mio problema sta nella somma dei vettori: in pratica, per quello che ho potuto vedere con il debug, il distruttore si intromette dopo le operazioni di copia e assegnamento andando a cancellare il primo numero del mio vettore. Ho visto che le operazioni vengono correttamente eseguite e l'assegnamento (credo che, come da mia intenzione, venga usato quello per spostamento) riesce ma, prima tornare all'istruzione successiva nel main, il distruttore cerca di distruggere il mio vettore, anche se è avvenuto un assegnamento per spostamento.


#include <iostream>

using namespace std;

class Vector{
public:
    Vector(int s);
    Vector(std::initializer_list<double> lst);
    Vector(const Vector& a); // costruttore di copia
    Vector& operator=(const Vector& a); // assegnamento di copia
    Vector(Vector&& a); // costruttore di spostamento
    Vector& operator=(Vector&& a); // assegnamento di spostamento
    double& operator[](int i);
    int size();
    ~Vector();
private:
    double *elem;
    int sz;
};

std::ostream& operator<<(ostream& os, Vector& vec){
for(int i=0; i<vec.size(); i++)
    os<<vec[i]<<" ";
return os;
}

Vector::Vector(int s){
    if(s<=0) throw length_error{"Vector::Vector"};
    elem=new double[s];
    sz=s;
}

Vector::Vector(std::initializer_list<double> lst)
    :elem{new double[lst.size()]}, sz{static_cast<int>(lst.size())}
    {
    copy(lst.begin(), lst.end(), elem);
    }

Vector::Vector(const Vector& a)
    :elem{new double[a.sz]}, sz{a.sz}
    {
    for(int i=0; i!=sz; i++)
        elem[i]=a.elem[i];
    }

Vector& Vector::operator=(const Vector& a){
double *p=new double[a.sz];
for(int i=0; i!=a.sz; i++)
    p[i]=a.elem[i];
delete[] elem;
elem=p;
sz=a.sz;
return *this;
}

Vector::Vector(Vector&& a)
    :elem{a.elem},
    sz{a.sz}
    {
    a.elem=nullptr;
    a.sz=0;
    }

Vector& Vector::operator=(Vector&& a){
    elem=a.elem;
    sz=a.sz;
    return *this;
    }

double& Vector::operator[](int i){
    if(i<0 || i>=size()) throw out_of_range{"Vector::operator[]"};
    return elem[i];
}

int Vector::size(){
    return sz;
}


Vector operator+(Vector x, Vector y) {

if(x.size()!=y.size())
    cout<<"Error\n";
Vector res(x.size());
for(int i=0; i!=x.size(); i++)
    res[i]=x[i]+y[i];
return res;
}

Vector::~Vector(){
    delete[] elem;
}

int main(){
    Vector vec(10), vec4={1,2,3,4,5,6,7,8,9,10};
    Vector vec2(vec4);
    Vector vec3(vec4);
    vec=vec2+vec3+vec4;
    cout<<vec;
    return 0;
}


Sapete illuminarmi sul funzionamento di questi meccanismi e come evitare quello che mi è capitato?

Grazie in anticipo.

4 Risposte

  • Re: Problemi col distruttore di una classe

    Hai fatto tutto giusto tranne un piccolo particolare:
    
    Vector& Vector::operator=(Vector&& a){
        // previene un memory leak dovuto al costruttore
        if (elem) delete[] elem; 
    
        elem=a.elem;
        sz=a.sz;
    
        // queste due righe.
        a.elem = nullptr;
        a.sz = 0;
        return *this;
        }
    
  • Re: Problemi col distruttore di una classe

    Grazie mille, sei stato gentilissimo, non mi ero accorto della svista; essendo nuovo al C++ non sapevo neanche che questo poteva 'ingannare' il distruttore. Già che ci siamo puoi spiegarmi bene come funziona l'automatismo del distruttore? E' totalmente automatico come una sorta di garbage collector o si può controllare esplicitamente?
  • Re: Problemi col distruttore di una classe

    Presumo volessi dire distruttore.
    Si, la chiamata del distruttore è automatica quando l'oggetto esce dallo scope (vale anche per copie temporanee), per quello quando si devono gestire puntatori si scrive come minimo il costruttore di copia e l'operatore di assegnamento. Se non si fa si ha una doppia delete del puntatore contenuto con conseguente crash (che è quello che avveniva nel tuo caso).
    Non è esattamente un garbage collector in quanto non si occupa specificatamente della memoria, infatti se tu non metti quel delete[] elem, il distruttore non fa nulla, ma il puntatore resta allocato. Il distruttore si occupa solo di "reclamare" le risorse quando non servono più.
    Può essere paragonato a qualcosa di simile a finalize() di Java, tuttavia a differenza di finalize() in C++ si sa sempre quando il distruttore è invocato (per questo è determinisco, a differenza di finalize() o del GC).
    Il controllo esplicito è possibile, ma questo è un argomento da C++ avanzato. Lascialo perdere per ora.
    Per approfondimenti su cosa fa il distruttore cerca Paradigma Raii su google.
  • Re: Problemi col distruttore di una classe

    Ok, grazie mille.
Devi accedere o registrarti per scrivere nel forum
4 risposte