C++ Aggregazione classi

di il
3 risposte

C++ Aggregazione classi

Salve ragazzi vorrei realizzare una relazione di aggregazione (quindi del tipo 'has a') fra due classi.
Non riesco a capire bene quali passi dovrei seguire. Da quel che ho capito la classe contenitore deve avere un puntatore a un oggetto di tipo contenuto, ma come funziona per il costruttore? E per le istanze?
Purtroppo non trovo spiegazioni esaurienti nè in rete nè sui libri di testo...potreste chiarirmi un po' le idee?
Grazie in anticipo

3 Risposte

  • Re: C++ Aggregazione classi

    "Has a" significa semplicemente che una classe A ne contiene una B. Il fatto che B possa essere un puntatore è un dettaglio implementativo.
    L'esempio minimale è:
    
    #include <string>
    class A {
        std::string var;
    };
    
    La classe A contiene una classe B di tipo std::string e come vedi non è necessario un puntatore per realizzare un contenimento in senso stretto.
  • Re: C++ Aggregazione classi

    A me interessava l'altro tipo, l'aggregazione per l'appunto. Quella in cui l'oggetto contenuto ha vita anche senza l'oggetto contenitore.
  • Re: C++ Aggregazione classi

    Per definizione una classe è un aggregato di tipi diversi.
    Che venga definito "per contenimento" il fatto di avere un tipo B fatto e finito al suo interno, e "per aggregazione" il fatto di avere un tipo B* al suo interno è solo un artificio lessicale per distinguere i due casi, ma non cambia la sostanza della cosa: cioè che una classe "contiene" qualcosa.
    (Un po' come il concetto di interfaccia in C++, che é talmente labile che per definirlo si è dovuto importarlo da Java.)
    Quella in cui l'oggetto contenuto ha vita anche senza l'oggetto contenitore.
    L'unico caso in cui ciò sia possibile è che l'oggetto contenuto sia dichiarato static, tuttavia tale oggetto rimane vincolato alla classe contenitore e non si può avere accesso senza passare da tale classe (se poi è un puntatore si rischiano disastri).
    Il caso "aggregazione" non è altro che la gestione di un puntatore da parte della classe contenitore (con tutti gli oneri del caso).
    
    class B {
        ...
    };
    
    class A {
        B* var;
        public:
            A() : var(new B) {}
            A(B* v) : var(v) {}
            ~A() { delete var;}
    };
    
    Il primo costrutture di A crea per default un oggetto di classe B e lo assegna a var;
    il secondo costruttore si aspetta un un puntatore a B pre creato e lo assegna a var.
    A poi avrà dei metodi per accedere ai metodi di B.
    Ovviamente se B è un'interfaccia pura (una o più funzioni virtuali=0), il primo costruttore di A non ha senso (mi riferisco all'altro post sulle funzioni virtuali pure) dato che non è possibile istanziare un'interfaccia pura.
    Ha senso invece (sempre nel primo costruttore) inizializzare var a 0, in quanto se l'oggetto A viene creato è legale fare una delete di un puntatore NULL (0):
    
    A() : var(0) {}
    ...
    int main(etc) {
        A a;
    } // nessun crash.
    
    e fornire un metodo per inizializzare il puntatore dopo la costruzione dell'oggetto A.
    Ad esempio qui C e D sono derivate dalla classe astratta B.
    
    A a;
    a.reset(new C);
    // oppure
    A aa(new D);
    
    Naturalmente la classe A dovrà avere un costruttore di copia e un operatore di assegnamento per evitare memory leak e/o crash dovuti alla mala copia di var da un oggetto all'altro.
    
    int main(etc) {
        A a;
        a.reset(new C);
        A aa = a;
    } // crash
    

    In questo caso sia aa.var sia a.var puntano alla stessa locazione di memoria, che può essere cancellata una volta sola. Poiché sia il distruttore di aa sia il distruttore di a vengono richiamati all'uscita, entrambi effettueranno una delete delle stessa zona di memoria: crash.
    Giusto per curiosità: ti serve per studio? E che compilatore usi?
Devi accedere o registrarti per scrivere nel forum
3 risposte