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?