PDA

View Full Version : [C++] RAII e ereditarietà: è corretta questa architettura?


Johnn
14-07-2008, 19:00
Per un progetto universitario devo realizzare in c++ una cosa del genere (in realtà le classi figlie sono di più ma il concetto non cambia):

http://img353.imageshack.us/img353/295/classdiagram1yv6.jpg (http://imageshack.us)

Ovviamente devo gesitre opportunamente le deallocazioni degli oggetti creati dinamicamente. Il problema è che molte delle classi figlie sono dipendenti tra loro ed hanno metodi che istanziano nuovi oggetti, ne modificano di già esistenti, ecc. In altre parole chi alloca non può deallocare un certo oggetto.
Per avere comunque una opportuna deallocazione della memoria non più in uso, ho letto in rete dell'idioma RAII e anche degli smart pointer. Vorrei evitare l'uso di quest'ultimi, ma penso sia adatta l'architettura seguente:

http://img179.imageshack.us/img179/417/classdiagramxb6.th.jpg (http://img179.imageshack.us/my.php?image=classdiagramxb6.jpg)

che ho adattato da questo esempio:

class WindowManager {
private:
Window* w;
public:
WindowManager() {
w = WindowFactory::createWindow();
}
Window* getWindow() {
return w;
}
~WindowManager() {
delete w;
}
}
class TestWindow {
void disegna() {
WindowManager m;
Window* w = m.getWindow();
/* utilizza w... */
} /* ora m è out of scope: w è deallocata */
}


preso da qui:

http://programminghacks.wordpress.com/category/c/

L'idea è che le classi A, B, ecc. hanno costruttori privati e quindi possono essere istanziate solo con le rispettive classi Manager: saranno queste che istanziate staticamente allocheranno e distruggeranno gli oggetti in maniera praticamente automatica.

Ovviamente se prima il codice di un metodo era ad esempio:


...
return new B(this->getAttribute());


Ora dovrà essere:


...
Manager b;
b.createB(this->getAttribute());
return b.getInstance();



Ho sbagliato qualcosa?

Grazie.

Ufo13
14-07-2008, 19:33
Non ti basta usare auto_ptr?

Ufo13
14-07-2008, 19:40
La tua architettura presenta comunque un po' di problemi.

Cosa succede con questo codice?


void disegna(Application& app) {
WindowManager m;
Window* w = m.getWindow();
delete w;
}

Cosa succede se passi w ad un oggetto come qui:
void disegna(Application& app) {
WindowManager m;
Window* w = m.getWindow();
app.setWindow(w);
/* utilizza w... */
}

Pensa poi cosa succederebbe se facessi una copia della factory

Johnn
14-07-2008, 22:44
Per quanto riguarda auto_ptr:
quando ho cercato in rete la soluzione al mio problema ovviamente mi ci sono imbattutto. Spesso è consigliato usare soluzioni più evolute che si appoggiano a librerie esterne (boost per esempio); ma come ho detto il mio codice è per un progetto universitario e aggiungo che la corretta deallocazione della memoria non è tra gli obiettivi principali (può sembrare strano ma ci sono diverse ragioni che non sto qui a spiegare). Per questo non ho voluto usare cose troppo potenti (penso che la soluzione corretta sia usare "i reference-counting smart pointer (RCSP), che tengono traccia del numero di puntatori alla risorsa gestita e permettono di garantire una garbage collection per una particolare risorsa. In C++ gli RCSP coincidono con std::tr1::shared_ptr<T>.") o che necessitino di includere librerie non standard. auto_ptr (so che è standard, ma avevo letto che era poco potente, ecc.) e l'ho scartato.

Tuttavia su tuo consiglio ho riletto qualcosa su come si usa e il problema per l'uso che ne devo fare io penso sia:

"If an auto_ptr is copied, the source loses the reference." (Wikipedia)

Quindi penso che comunque non lo possa usare, in quanto gli oggetti del mio programma sono spesso condivisi tra loro, passati come parametri ad altri costruttori/metodi. Giusto?

Per la seconda parte del post:
ho usato l'esempio che ho postato solo per trarne spunto, non devo fare Window o simili (perché hai usato come parametro del metodo "Application& app"?);
per questo:
void disegna(Application& app) {
WindowManager m;
Window* w = m.getWindow();
delete w;
}
forse il problema non c'è in quanto la delete nei metodi delle classi non metto mai (perché dovrei?); nel main, o chi usasse le mie classi, teoricamente potrebbe essere fatto, ma posso tranquillamente assumere che non accada mai.

Per quast'altro
void disegna(Application& app) {
WindowManager m;
Window* w = m.getWindow();
app.setWindow(w);
/* utilizza w... */
}


la situazione è effettivamente delicata.

Intanto riporto l'esempio in uno scenario più simile al mio:

Abstract* /*Puntatore di tipo della classe astratta padre di ogni altra classe A, B, ecc.*/ method(B ib) {
ManagerA m;
A* ia = m.getInstance();
ia->setAttribute(ib);
this->setAttribute3(ia);
return this;
}


Il guaio è che la classe manager può deallocare la classe gestita mentre essa è puntata da qualche altra parte, vero? Bel problema. :muro:

Il quesito che mi ha fatto fare tutto ciò è che mi sono chiesto se in c++ sia possibile risparmiare memoria facendo puntare ad una stessa area di memoria più puntantori invece di creare sempre nuovi oggetti (il problema non si avrebbe a questo punto, sempre che gli oggetti condivisi siano effettivamente identici) e deallocare opportunamente il tutto, ma pare impossibile senza usare garbage collector o smart pointer. :muro:

Grazie mille ancora.

Johnn
17-07-2008, 18:00
Dati i problemi incontrati fino ad ora, sto optando per l'uso di un garbage collector. Più precisamente uso questo:

http://www.hpl.hp.com/personal/Hans_Boehm/gc/