PDA

View Full Version : [C++] Da puntatore a reference.


Opcode
06-08-2010, 14:16
Giorno,
stò pianificando dei metodi per restituire una reference degli oggetti della classe, invece di restituirne i puntatori per maggiore sicurezza ed evitare che possano essere cancellati, ho quindi un metodo tipo questo:
Object &Classe::object()
{
return *_object; // oggetto privato
}

Mi chiedevo, quello che restituisco è effettivamente una reference a _object oppure ne restituisco una copia?

_object ho dovuto dichiararlo come puntatore perchè la sua inizializzazione avviene in un metodo chiamato "init" e non nel costruttore di Classe.
Se volessi dichiarare in Classe:

Classe {
public:
// ...
Object object;
};
Come potrei fare per catturare eventualmente le eccezioni del costruttore di Object?
object in quest'ultimo caso dovrebbe essere inizializzato nel costruttore di Classe giusto?
Se cosi fosse mi basterebbe dichiarare un blocco try catch nel costruttore e gestire l'eventuale eccezione... ma come?

Aspetto vostre notizie :)
Grazie in anticipo.

Wing_Zero
06-08-2010, 18:01
Allora innanzitutto l'oggetto "object" devi dichiararlo come variabile di classe, all'interno dell'header della classe e non crearlo all'interno del costruttore.
Altrimenti avresti uno scope locale al costruttore e non puoi restituirlo.

Poi quando lo restituisci: return *object, (se hai creato un puntatore ad object e "object" è l'indirizzo), altrimenti return object (se object è l'oggetto stesso).

nel caso vuoi instanziare l'oggetto all'interno del metodo stesso, devi scrivere
Object &Classe::object()
{
Object* object = new object();
return object;
}

in questo caso passi il riferimento e non la copia dell'oggetto, ma avendolo creato tramile la "new", l'oggetto viene allocato nello heap e non muore mai. neanche se distruggi il parent. Ti dovrai quindi ricordare di distruggerlo (" delete(object)" ) in seguito.

Cmq datti una letta a qualche libro per iniziare il C++...non ti puo' fare che bene.

Opcode
06-08-2010, 18:20
Temo tu mi abbia frainteso.
Si object è un membro della classe, privato.
Nella dichiarazione della classe lo definisco come puntatore perchè la vera inizializzazione tramite new avviene non nel costruttore di Classe ma in un metodo chiamato "init".
Quello deve quindi essere il primo metodo, cosi crea l'oggetto nello heap e assegna al puntatore il suo indirizzo.
Ho però bisogno di restituire una reference, non una copia (non c'e n'è necessità) nè un puntatore, che potrebbe essere cancellato.

Il mio dubbio sorgeva dal fatto che sino ad oggi tutti gli esempi di funzioni che restituiscono una reference sono cosi strutturate:

Object &Class::Reference()
{
Object &ref = *_object; // object è un membro della classe
return ref;
}

Mentre io per semplificare il tutto, e non trovando una necessità di rifare tutto faccio più semplicemente:

Object &Class::Reference()
{
return *_object;
}

Ovviamente il compilatore non mi dà ne warning, ne errori. Però il mio dubbio era che in realtà non facendo quel passaggio di creare io stesso un puntatore reference, fosse restituita una copia di _object, e il compilatore non segnala alcun errore pur non essendo una reale reference, tutto qui.
La seconda domanda invece era:
Se nella dichiarazione (non definizione) della classe anzichè mettere:
Object *_object;
Da inizializzare poi nel metodo "init" mettessi più semplicemente
Object _object;
In che modo posso catturare eventualmente una eccezione sollevata dal costruttore della classe Object?
IMHO è da fare nel costruttore della classe che dichiara l'oggetto _object, ma non essendo io a inizializzare l'oggetto ho qualche perplessità su come catturare quell'eccezione.

Se non sono stato chiaro, faccio un piccolo esempio.
Grazie per la risposta.

cionci
06-08-2010, 18:52
Object &Classe::object()
{
Object* object = new object();
return object;
}

E chi lo dealloca object ???
Questo è un memory leak ;)

Object &Class::Reference()
{
Object &ref = *_object; // object è un membro della classe
return ref;
}

Mentre io per semplificare il tutto, e non trovando una necessità di rifare tutto faccio più semplicemente:

Object &Class::Reference()
{
return *_object;
}


Sono equivalenti ;)
Come potrei fare per catturare eventualmente le eccezioni del costruttore di Object?
object in quest'ultimo caso dovrebbe essere inizializzato nel costruttore di Classe giusto?
Se cosi fosse mi basterebbe dichiarare un blocco try catch nel costruttore e gestire l'eventuale eccezione... ma come?
Spiega un po' meglio questo punto...

Albi89
06-08-2010, 19:35
Il codice che hai utilizzato va benissimo, e non ritorna una copia, non ti preoccupare ;)

Per la seconda domanda, hai bisogno di un "function try block", cioè di associare il try non ad un blocco ma all'intera funzione:
MyClass::MyClass()
try : object() {
//Corpo del costruttore
}
catch (myObjectException &e){
//Gestione dell'eccezione
}

Opcode
06-08-2010, 19:40
Sono equivalenti ;)
Ooottimo :) Era quello che mi serviva sapere, ho cercato più volte ma, probabilmente a scopo didattico, il risultato era simile in tutti i casi.
Spiega un po' meglio questo punto...
Allora, supponiamo che io ho la seguente classe:

class Window {
public:
Window() { throw 1; } // supponiamo una eccezione
};

class Handler {
public:
Handler() {};
Window window; // (1) -- alla costruzione lancerà un'eccezione
};

(1) Come catturo l'eccezione sollevata dal costruttore di Window?

Se avessi un puntatore, farei tipo:

// Window *window; // dichiarato precedentemente
Handler::Handler() {
try {
window = new Window;
}
catch (Exception &ex) {
// gestisci
}
}

Ma in quel caso l'oggetto viene creato automaticamente dal costruttore di Handler quindi come faccio a cattuare l'eccezione di Window::Window?

Il codice che hai utilizzato va benissimo, e non ritorna una copia, non ti preoccupare ;)
:D ottimo.

Per la seconda domanda, hai bisogno di un "function try block", cioè di associare il try non ad un blocco ma all'intera funzione:
MyClass::MyClass()
try : object() {
//Corpo del costruttore
}
catch (myObjectException &e){
//Gestione dell'eccezione
}
Eccolo qui, benissimo, ero indeciso se questo metodo avrebbe funzionato o meno, e comunque preferisco sempre avere un feedback per non fare qualche cacchiata.

Se io non inizializzo esplicitamente object() ? Funzionerà ugualmente?
Grazie mille per la risposta :)

Albi89
06-08-2010, 20:18
Se io non inizializzo esplicitamente object() ? Funzionerà ugualmente?
Grazie mille per la risposta :)

Onestamente penso di sì ma non ci giurerei... meriterebbe una prova ma in questo momento confesso di aver bootato in Windows per giocare a Dragon Age e non ho nulla installato per provare :sofico:

"A rigore" dovrebbe funzionare, nell'indecisione comunque aggiungere la chiamata al costruttore non costa molto e rende più chiaro "da chi" ti aspetti un'eccezione.

Un problema più interessante è che, quando si associa un "function try block" ad un costruttore, al termine del blocco catch, se questi non ha propagato l'eccezione ricevuta (o non ne ha lanciata una nuova), l'eccezione originaria è comunque propagata: in soldoni, quando fallisce il costruttore di una sottoparte, deve fallire il costruttore dell'intero oggetto -quindi ricordati di avere un blocco try associato alla costruzione di MyClass, e possibilmente lancia una eccezione "ad hoc" nel blocco catch!-.

Una questione simile dovrebbe valere anche per i distruttori, anche se ha implicazioni meno significative (una eccezione in un distruttore dovrebbe sempre terminare il programma, e in generale il codice di un distruttore è meno propenso a fallire).

Wing_Zero
06-08-2010, 21:12
E chi lo dealloca object ???
Questo è un memory leak ;)

Sono equivalenti ;)

Spiega un po' meglio questo punto...

come chi lo dealloca?
passi il rifermimento all'oggetto,e poi delete(object)

Opcode
06-08-2010, 21:16
Confermo che funziona :D
Comunque si, per maggiore chiarezza lo inizializzerò esplicitamente.
Un problema più interessante è che, quando si associa un "function try block" ad un costruttore, al termine del blocco catch, se questi non ha propagato l'eccezione ricevuta (o non ne ha lanciata una nuova), l'eccezione originaria è comunque propagata: in soldoni, quando fallisce il costruttore di una sottoparte, deve fallire il costruttore dell'intero oggetto -quindi ricordati di avere un blocco try associato alla costruzione di MyClass, e possibilmente lancia una eccezione "ad hoc" nel blocco catch!-.
:eek:
Hai fatto benissimo a dirmelo, non ne ero a conoscenza.
Sapevo invece che se c'è un problema nel costruttore e hai allocato qualche risorsa, l'eccezione non chiama il distruttore perchè l'oggetto non è stato creato, e quindi ci vuole attenzione.

Riguardo a questo problema, ti giro una nuova domanda:
e se MyClass fosse un singleton? :D

La classe viene inizializzata staticamente, come potrebbe gesitre il throw del proprio costruttore?


Una questione simile dovrebbe valere anche per i distruttori, anche se ha implicazioni meno significative (una eccezione in un distruttore dovrebbe sempre terminare il programma, e in generale il codice di un distruttore è meno propenso a fallire).
Uhm, farò qualche test anche sui distruttori, per vedere un po' che succede nei casi peggiori.

Grazie.

tomminno
06-08-2010, 21:26
come chi lo dealloca?
passi il rifermimento all'oggetto,e poi delete(object)

Per me è da pazzi pensare di deallocare un riferimento (comunque eventualmente delete &object; ). Scusa la franchezza.
Anche perchè poi tutti gli eventuali riferimenti avrebbero un comportamento indefinito, il crash del programma è assicurato.
Infine chi ti dice che l'oggetto a cui punta il riferimento è stato allocato con una new?

Albi89
06-08-2010, 21:44
come chi lo dealloca?
passi il rifermimento all'oggetto,e poi delete(object)

Allocazione è deallocazione vanno SEMPRE svolte allo stesso livello di "astrazione": allocare all'interno di una classe e lasciare la responsabilità della deallocazione all'utente è una pratica da evitare assolutamente; al massimo si può ritornare un oggetto gestore che deallochi il riferimento quando out of scope.

fero86
06-08-2010, 21:45
Infine chi ti dice che l'oggetto a cui punta il riferimento è stato allocato con una new? chi ti dice che lo sia quello gestito tramite puntatore?

Albi89
06-08-2010, 21:58
Riguardo a questo problema, ti giro una nuova domanda:
e se MyClass fosse un singleton? :D


A livello tecnico non dovrebbe cambiare niente; posto Instance() il metodo mediante il quale richiedi l'istanza e _instance l'istanza membro, dovresti probabilmente fare qualcosa di simile:
MySingleton* MySingleton::Instance() {
if (_instance == 0) {
try {
_instance = new MySingleton();
}
catch (mySingletonConstructionException) {
//...
throw;
}
}
return _instance;
}
Cioè un approccio tradizionale (senza function try block), ma avendo cautela di far comportare la clausola catch come quella associata a un costruttore (cioè propagando l'eccezione: d'altra parte quella parte del metodo Instance() è deputata alla costruzione è in caso di fallimento non ha mai senso ritornare il puntatore all'istanza).

Ovviamente, il costruttore MySingleton::MySingleton() può avere o non avere un function try block associato: lo avrà se inizializza altri oggetti (cioè se eredita da altre classi o se inizializza membri con una sintassi constructor initializer, ossia i fatidici ":"), altrimenti conterrà semplicemente uno statement try.

tomminno
06-08-2010, 22:44
chi ti dice che lo sia quello gestito tramite puntatore?

Infatti utilizzi il riferimento e non hai più dubbi su chi deve gestire la vita dell'oggetto.

Wing_Zero
07-08-2010, 00:44
Per me è da pazzi pensare di deallocare un riferimento (comunque eventualmente delete &object; ). Scusa la franchezza.
Anche perchè poi tutti gli eventuali riferimenti avrebbero un comportamento indefinito, il crash del programma è assicurato.
Infine chi ti dice che l'oggetto a cui punta il riferimento è stato allocato con una new?
Deallocando un riferenmento (Comunque delete object e non &object, in quanto avevo specificato che object era l'indirizzo e *object l'oggetto) devi sape a priori che è stato allocato con una new. altrimenti non avrebbe senso deallocarlo tamire un riferimento se la variabile fosse temporanea nello stack e non nell'heap. mi pare ovvio.
Allocazione è deallocazione vanno SEMPRE svolte allo stesso livello di "astrazione": allocare all'interno di una classe e lasciare la responsabilità della deallocazione all'utente è una pratica da evitare assolutamente; al massimo si può ritornare un oggetto gestore che deallochi il riferimento quando out of scope.
Concordo che non è la pratica migliore in assoluto, ma a volte non si puo' evitare.

cionci
07-08-2010, 06:28
Deallocando un riferenmento (Comunque delete object e non &object, in quanto avevo specificato che object era l'indirizzo e *object l'oggetto) devi sape a priori che è stato allocato con una new. altrimenti non avrebbe senso deallocarlo tamire un riferimento se la variabile fosse temporanea nello stack e non nell'heap. mi pare ovvio.
object è un puntatore ? Quindi addirittura torno un puntatore allocato con malloc ritornato per riferimento ?
A questo punto le domande sono tre:
- chi dealloca il puntatore
- chi dealloca la memoria puntata dal puntatore
- che tipo di deallocazione va fatta ? una delete o una delete[] sui dati ? Chi ti garantisce che la venga fatta quella giusta ?
Concordo che non è la pratica migliore in assoluto, ma a volte non si puo' evitare.
Imho va semplicemente evitata. Se si è costretti ad usarla significa che bisogna fare un refactoring del nostro codice.

Opcode
07-08-2010, 15:25
Quindi il controllo delle eccezioni mi forza ad usare un puntatore come singleton, cioè se io volessi fare qualcosa come:

Class MySingleton {
private:
static MySingleton _instance;
MySingleton() {};
MySingleton(const MySingleton&); //disabilito il copy constructor
public:
static MySingleton &instance() { return _instance; }
};
// inizializzo il singleton
MySingleton MySingleton::_instance; // (1)

Mi chiedevo se fosse quindi possibile intercettare l'eccezione nel punto (1) essendo in ambito globale. Ovviamente qualora non fosse possibile procederei come hai descritto tu, ma sempre restituendo una reference per evitare il delete.


Cioè un approccio tradizionale (senza function try block), ma avendo cautela di far comportare la clausola catch come quella associata a un costruttore (cioè propagando l'eccezione: d'altra parte quella parte del metodo Instance() è deputata alla costruzione è in caso di fallimento non ha mai senso ritornare il puntatore all'istanza).
Beh se fallisce la creazione del singleton non sarebbe opportuno terminare l'esecuzione del programma con uno status di errore? Sempre supponendo che il singleton non venga creato attraverso un'altro oggetto, altrimenti si potrebbe sollevare l'eccezione e riprovare a crearlo.

EDIT: ora che ci ripenso forse una soluzione c'è. Se non erro in mancanza di un handler per l'eccezione viene chiamato terminate() dalla <exception>, quindi se io gestissi in quel punto l'eccezione eventualmente generata, dovrebbe funzionare quanto meno per evitare un crash perchè non ho gestito l'eccezione, non credi? Io intanto faccio qualche test :)
Scherzavo, non posso cattuare l'eccezione :(

marco.r
07-08-2010, 18:00
come chi lo dealloca?
passi il rifermimento all'oggetto,e poi delete(object)

Pessima idea. Quando un oggetto viene ritornato per riferimento l'assunzione implicita e' che chi te la fornito ne mantiene comunque la patria potesta'.
Un po' come un bambino che ti presta il pallone per giocarci a calcio assieme. Pui fare quello che vuoi, ma non puoi portartelo via, e quando se ne va l'altro, il pallone sparisce assieme a lui.
Per motivi analoghi non dovresti prenderne il puntatore e salvarlo da altre parti, salvo rare eccezioni.

(Discorso analogo si potrebbe fare per gli argomenti passati per riferimento, che in generale non si puo' pensare abbiano vita maggiore della chiamata stessa )

marco.r
07-08-2010, 18:08
Allocazione è deallocazione vanno SEMPRE svolte allo stesso livello di "astrazione": allocare all'interno di una classe e lasciare la
responsabilità della deallocazione all'utente è una pratica da evitare assolutamente;

Direi piuttosto che e' importante che sia chiaro chi e' il proprietario dell'oggetto ritornato.

marco.r
07-08-2010, 18:19
Un problema più interessante è che, quando si associa un "function try block" ad un costruttore, al termine del blocco catch, se questi non ha propagato l'eccezione ricevuta (o non ne ha lanciata una nuova), l'eccezione originaria è comunque propagata: in soldoni, quando fallisce il costruttore di una sottoparte, deve fallire il costruttore dell'intero oggetto -quindi ricordati di avere un blocco try associato alla costruzione di MyClass, e possibilmente lancia una eccezione "ad hoc" nel blocco catch!-.

Starei attento a lanciare eccezioni nei costruttori. Il primo problema e' che devi fare pulizia di quanto hai gia' inizializzato. Il secondo, ben piu' importante, e' che devi farlo in ogni oggetto che usi una istanza di questa classe come attributo.
Ci si puo' stare attenti ma e' un sacco di lavoro


Una questione simile dovrebbe valere anche per i distruttori, anche se ha implicazioni meno significative (una eccezione in un distruttore dovrebbe sempre terminare il programma, e in generale il codice di un distruttore è meno propenso a fallire).
Un'eccezione in un distruttore e' una ricetta sicura per il disastro, visto che ti puoi trovare in situazioni in cui viene lanciata un'eccezione durante lo stack unwinding.
Direi meglio evitare.
(e se invece l'oggetto era garbage collected che senso ha ?)

fero86
07-08-2010, 20:14
Direi piuttosto che e' importante che sia chiaro chi e' il proprietario dell'oggetto ritornato. sempre di chiacchiere da corridoio si tratta: cos'é in C++ un "proprietario"? e cos'é un "livello di astrazione"?

dovendo per forza palinfrascare agilmente io avrei preferito la definizione di Albi89 :D

fero86
07-08-2010, 20:17
Starei attento a lanciare eccezioni nei costruttori. Il primo problema e' che devi fare pulizia di quanto hai gia' inizializzato. Il secondo, ben piu' importante, e' che devi farlo in ogni oggetto che usi una istanza di questa classe come attributo.
Ci si puo' stare attenti ma e' un sacco di lavoro veramente no, anzi, avviene tutto in automatico: se la costruzione di un oggetto fallisce con un'eccezione C++ (non SEH) vengono distrutti tutti e soli i sotto-oggetti* costruiti fino a quel momento e vengono distrutti in ordine inverso rispetto alla creazione. sempre che il compilatore sia compliant naturalmente.

*per sotto-oggetti intendo i campi di tipo classe.

EDIT - anzi: se devo rappresentare in un programma una risorsa la cui creazione o acquisizione puó fallire, io preferisco sempre lanciare eccezioni dal costruttore, altrimenti il programma diventa piu complesso perché per quella classe devo prevedere due possibili stati: "creato ma non inizializzato" e "creato e inizializzato". i controlli "assert" a quel punto fioriscono.

EDIT2 - anzi, tre stati: "creato ma non inizializzato", "creato e inizializzato con successo" e "creato ma inizializzazione fallita".

fero86
07-08-2010, 20:21
Un'eccezione in un distruttore e' una ricetta sicura per il disastro, visto che ti puoi trovare in situazioni in cui viene lanciata un'eccezione durante lo stack unwinding. qualche ulteriore dettaglio?

marco.r
07-08-2010, 21:51
qualche ulteriore dettaglio?
Devi gestire contemporaneamente due eccezione e il programma qualsiasi scelga perde informazioni.
Esempio:

#include <iostream>

struct Bar{};

struct Foo
{
~Foo(){ throw Bar(); }
};

void g()
{
throw 42;
}

void f()
{
try
{
Foo f;
g();
}
catch( int )
{
std::cout << "Got int" << std::endl;
}
}


int main()
{
try
{
f();
}
catch( const Bar& b )
{
std::cout << "Got Bar" << std::endl;
}
}

Quando g() lancia l'eccezione, lo stack viene svolto, ad un certo punto pero' deve distruggere Foo che lancia a sua volta un'eccezione. Se gestisci una delle due poi non hai piu' il contesto per gestire l'altra, e infatti il programma finisce con un terminate()

marco.r
07-08-2010, 21:53
sempre di chiacchiere da corridoio si tratta: cos'é in C++ un "proprietario"?

Nel mio contesto, un altro oggetto che ha la responsabilita' di deallocare l'istanza in questione, ne piu' ne meno.

fero86
07-08-2010, 22:48
Esempio: [...] vero, sono d'accordo che lanciare eccezioni dai distruttori sia del tutto da evitare visto che i distruttori vengono invocati anche in caso di stack unwinding provocato da un'eccezione, infatti chiedevo chiarimenti perché avevo letto male questo post: Un'eccezione in un distruttore e' una ricetta sicura per il disastro, [...] avevo letto "costruttore" anziché "distruttore".



Nel mio contesto, un altro oggetto che ha la responsabilita' di deallocare l'istanza in questione, ne piu' ne meno. non é detto che un oggetto sia allocato sotto forma di variabile membro di un'altra classe e non é detto neanche che esso sia stato allocato dinamicamente dal codice di una funzione membro.

parlando a livello intuitivo non é detto che il "proprietario" di un oggetto sia un altro oggetto. il concetto di livello di astrazione mi piaceva di piu :Prrr:

Wing_Zero
08-08-2010, 10:19
object è un puntatore ? Quindi addirittura torno un puntatore allocato con malloc ritornato per riferimento ?
A questo punto le domande sono tre:
- chi dealloca il puntatore
- chi dealloca la memoria puntata dal puntatore
- che tipo di deallocazione va fatta ? una delete o una delete[] sui dati ? Chi ti garantisce che la venga fatta quella giusta ?

Imho va semplicemente evitata. Se si è costretti ad usarla significa che bisogna fare un refactoring del nostro codice.

chi dealloca l'oggetto?
il chiamante quando sa che non gli serve più.

Chi dealloca il puntatore?
Scusa, ma come si dealloca un puntatore? al massimo lo metti =0.

esempio pratico: factory che crea una instanza di un oggetto di tipo di un interfaccia generico. Voglio vedere come fai a far distruggere l'oggetto in questo caso alla classe stessa -.-".

tomminno
08-08-2010, 11:23
chi dealloca l'oggetto?
il chiamante quando sa che non gli serve più.


Questo è un ragionamento proprio del C.
Chi ti dice che il chiamato non utilizzi quello stesso oggetto per i fatti suoi? Che non abbia passato quello stesso oggetto ad altri chiamanti?
Uno dei principi base della programmazione ad oggetti è l'incapsulamento, io chiamante non devo conoscere il funzionamento interno del chiamato. Secondo il tuo ragionamento invece il chiamante deve conoscere il comportamento interno del chiamato. In caso di modifiche interne a quest'ultimo, sei obbligato a rivedere anche il primo.


esempio pratico: factory che crea una instanza di un oggetto di tipo di un interfaccia generico. Voglio vedere come fai a far distruggere l'oggetto in questo caso alla classe stessa -.-".

Per questo esiste da sempre una classe chiamata auto_ptr (ora deprecata in favore di shared_ptr). Con auto_ptr espliciti che il controllo sull'esistenza dell'oggetto passa al chiamante.
Chi ti dice che il Factory non sia di tipo "singleton" ovvero che ritorna sempre la stessa istanza a parità di parametri in ingresso? Se il tuo chiamante ne facesse la delete ti ritroveresti sicuramente nei guai.

MenageZero
08-08-2010, 12:49
scusandomi per l'intromissione ... :p

Questo è un ragionamento proprio del C.
Chi ti dice che il chiamato non utilizzi quello stesso oggetto per i fatti suoi? Che non abbia passato quello stesso oggetto ad altri chiamanti?
Uno dei principi base della programmazione ad oggetti è l'incapsulamento, io chiamante non devo conoscere il funzionamento interno del chiamato. Secondo il tuo ragionamento invece il chiamante deve conoscere il comportamento interno del chiamato. In caso di modifiche interne a quest'ultimo, sei obbligato a rivedere anche il primo.

non vedo la necessità di porre obiezioni tanto "in grande":
per esempio, quando ritorni un tipo per valore non è forse la dichiarazione stessa del metodo che ti dice che ogni volta avrai una nuova istanza ?

allo stesso modo, in una situazione in cui può avere senso, come appunto factory in cui per il tipo prodotto è usata a modo di interfaccia una classe astratta, parte della definizione(semantica) formale come api di un metodo può benissimo essere che ti restituisce un puntatore ad una nuova istanza o invece sempre alla stessa, e la cosa essere tranquillamente parte della documentazione di progetto; da qui ce ne passa dal dire che il chiamante sia fortemente dipendente dall'implementazione del chiamato.

andiamo, ci sono api anche molto blasonate con le quali c'è una lista di cose che devi sapere (e quando la documentaziome delle medesime include la lista completa già va di lusso :asd: ) quando passi loro un puntatore o te ne ritornano uno, per evitare potenziali "effetti collaterali" ...

non voglio sostenere che non sia opportuno o sia indifferente distruggere un oggetto sullo heap con uno statement parte della stessa classe che lo ha creato, per carità, ma non sempre è possibile applicare tale "regola"

tantomeno mi voglio addentrare su cosa sia più "elegante" o meno etc, ma francamente più passa il tempo meno mi convince che per ogni cosa esista la regoletta universale che va sempre bene e che se "sgarri" allora devi buttare via tutto :D

Per questo esiste da sempre una classe chiamata auto_ptr (ora deprecata in favore di shared_ptr). Con auto_ptr espliciti che il controllo sull'esistenza dell'oggetto passa al chiamante.
Chi ti dice che il Factory non sia di tipo "singleton" ovvero che ritorna sempre la stessa istanza a parità di parametri in ingresso? Se il tuo chiamante ne facesse la delete ti ritroveresti sicuramente nei guai.

in un esempio come quello discusso aggiungere l'uso di auto_prt o chi per esso
può sicuramente essere un'ottima idea, ma personalmente lo vedo più come un miglioramento di dettaglio rispetto ai concetti discussi:

già solo il fatto che la factory ti ritorni (per valore ovviamente) un oggetto auto_ptr ti dice che creerà una nuova istanza ogni volta, un miglioramento perché è la definizione stessa del metodo che ti esclude l'opzione singleton rispetto al ritornare un puntatore, ma non è certo un ipotetico livello di astrazione in più che ti permette id avere indifferentemente la factory normale oppure singleton senza modificare né la definizioen del metodo né il chiamante;

poi cmq l'oggetto sullo heap verrebbe distrutto quando l'oggetto di tipo auto_ptr va fuori scope, quindi nel chiamante e cmq non nel codice classe che lo ha creato, la nostra "regola aurea" potrebbe in fondo ancora lagnarsene formalmente, metti che per caso la situazione lato factory fosse molto complessa ed il codice che crea effettivamente l'istanza non sa che poi verrà passata esternamente trmite un wrapper del puntatore che "takes ownership" dell'istanza ...se ci vogliamo arrovellare concettualmente non cambuia molto rispetto alla questione iniziale

inoltre, tanto per rimanere in tema di factory e "regole assolute" e signleton o meno, trovi pure tanti che predicano animatamente contro il singleton pattern,
qundi in questo esempio ci sarebbe pure un'ulteriore "regola assoluta" che andrebbe a risolvere la diatriba "a monte" anche se il metodo in questione ritornasse solo un normale puntatore :sofico:

ps: se il l'applicazione del concetto di definizione di un tipo come interfaccia ed il pattern della factory sono in voga ormai da molti anni, consolidati e che io sappia tuttora molto usati e con "reputazione" positiva, ed allo stesso tempo il c++ non implementa esplicitamente il concetto di interfaccia, in fondo non è certo colpa degli utilizzatori del c++ :D

pps: una precisazione, devo scappare di fretta e non ho tempo di correggere bene la forma del post, temo di aver unsato una o più volte la parola "definizione" al posto di "dichiarazione" relativamente ad un metodo (spesso "definizione" è usato come sinonimo di implementazione del metodo)

tomminno
08-08-2010, 18:56
non vedo la necessità di porre obiezioni tanto "in grande":
per esempio, quando ritorni un tipo per valore non è forse la dichiarazione stessa del metodo che ti dice che ogni volta avrai una nuova istanza ?


Ma la discussione era partita dal suggerimento di istanziare in un metodo un oggetto nell'heap e di ritornarne un riferimento che poi sarà deallocato dal chiamante.


andiamo, ci sono api anche molto blasonate con le quali c'è una lista di cose che devi sapere (e quando la documentaziome delle medesime include la lista completa già va di lusso :asd: ) quando passi loro un puntatore o te ne ritornano uno, per evitare potenziali "effetti collaterali" ...


Tipo? Io conosco solo le Xerces che essendo un ibrido C/C++ ti ritornano puntatori che il più delle volte devi deallocarti.
Ma certamente non esiste una sola API che ti ritorna un riferimento e nella documentazione ti dice che quell'oggetto te lo devi deallocare.


non voglio sostenere che non sia opportuno o sia indifferente distruggere un oggetto sullo heap con uno statement parte della stessa classe che lo ha creato, per carità, ma non sempre è possibile applicare tale "regola"


Se non puoi ci sono nel linguaggio le alternative per indicare tale circostanza, quindi perchè non usarle?
Se non viene fatto il più delle volte è perchè trovi una strutturazione molto alla C.


tantomeno mi voglio addentrare su cosa sia più "elegante" o meno etc, ma francamente più passa il tempo meno mi convince che per ogni cosa esista la regoletta universale che va sempre bene e che se "sgarri" allora devi buttare via tutto :D


Sai un programma lo puoi sempre scrivere tutto nel main oppure strutturare ad oggetti applicando tutti i pattern di questo mondo. Il programma può funzionare benissimo in entrambi i casi.
Per questo si parla di best practices.


poi cmq l'oggetto sullo heap verrebbe distrutto quando l'oggetto di tipo auto_ptr va fuori scope, quindi nel chiamante e cmq non nel codice classe che lo ha creato, la nostra "regola aurea" potrebbe in fondo ancora lagnarsene formalmente,


E questo che c'entra? Anche un oggetto ritornato per valore viene deallocato quando va fuori dallo scope nel chiamante e allora?


metti che per caso la situazione lato factory fosse molto complessa ed il codice che crea effettivamente l'istanza non sa che poi verrà passata esternamente trmite un wrapper del puntatore che "takes ownership" dell'istanza ...se ci vogliamo arrovellare concettualmente non cambuia molto rispetto alla questione iniziale


Infatti esiste da quel dì una classe chiamata shared_ptr, devi tornare un puntatore e non sai come viene usato? Torna uno shared_ptr e vedrai che risolverai molti dei tuoi problemi. Non a caso auto_ptr è deprecato.


inoltre, tanto per rimanere in tema di factory e "regole assolute" e signleton o meno, trovi pure tanti che predicano animatamente contro il singleton pattern,


Direi che questo c'entra poco con il discorso.
E' vero che tanti dicono male del singleton, ma in un programma ci sono delle variabili effettivamente globali, tipo le configurazioni.
Oppure pensa solo all'MVC se dei controller devono condividere lo stesso Model a chi chiederanno l'istanza del Model? Se non lo fai con un factory "singleton" io non ho molte altre alternative, se non quella che gli venga passato dal chiamante, ma non necessariamente il chiamante è in un contesto tale da avere un riferimento al Model richiesto.
Qualcuno ha altre alternative?


qundi in questo esempio ci sarebbe pure un'ulteriore "regola assoluta" che andrebbe a risolvere la diatriba "a monte" anche se il metodo in questione ritornasse solo un normale puntatore :sofico:


Sei te che l'hai presa come "regola assoluta", qui stiamo parlando di "best practice".
Sennò perchè non consigliare di scrivere tutto nel main così ci si evitano direttamente chiamate a funzioni o metodi con tutti gli eventuali problemi della gestione del ritorno?


ps: se il l'applicazione del concetto di definizione di un tipo come interfaccia ed il pattern della factory sono in voga ormai da molti anni, consolidati e che io sappia tuttora molto usati e con "reputazione" positiva, ed allo stesso tempo il c++ non implementa esplicitamente il concetto di interfaccia, in fondo non è certo colpa degli utilizzatori del c++ :D


:confused: E questo che c'entra? Non ci sono certo problemi nell'implementare interfacce in C++, nè tanto meno factory.

marco.r
08-08-2010, 19:59
veramente no, anzi, avviene tutto in automatico: se la costruzione di un oggetto fallisce con un'eccezione C++ (non SEH) vengono distrutti tutti e soli i sotto-oggetti* costruiti fino a quel momento e vengono distrutti in ordine inverso rispetto alla creazione. sempre che il compilatore sia compliant naturalmente.

Hai ragione, mi sono sbagliato.



EDIT - anzi: se devo rappresentare in un programma una risorsa la cui creazione o acquisizione puó fallire, io preferisco sempre lanciare eccezioni dal costruttore, altrimenti il programma diventa piu complesso perché per quella classe devo prevedere due possibili stati: "creato ma non inizializzato" e "creato e inizializzato". i controlli "assert" a quel punto fioriscono.

EDIT2 - anzi, tre stati: "creato ma non inizializzato", "creato e inizializzato con successo" e "creato ma inizializzazione fallita".
[/quote]
Una alternativa e' forzare l'uso di una factory, con costruttori privati e friend factory. In quel modo puoi inizializzare come vuoi ed evitare qualsiasi stato invalido (ed avere dei costruttori "virtuali")

marco.r
08-08-2010, 20:02
parlando a livello intuitivo non é detto che il "proprietario" di un oggetto sia un altro oggetto. il concetto di livello di astrazione mi piaceva di piu :Prrr:

L'oggetto da qualche parte deve stare, che sia lo stack delle chiamate, una variabile globale o un qualsiasi struttura dati. Se il puntatore all'oggetto e' presente in piu' parti bisogna decidere dove/da chi deve venire deallocato (o ricorrere a oggetti tipo shared_ptr), Il senso del mio intervento era questo, niente di piu'.

MenageZero
08-08-2010, 21:39
Ma la discussione era partita dal suggerimento di istanziare in un metodo un oggetto nell'heap e di ritornarne un riferimento che poi sarà deallocato dal chiamante.

che è quello che devi fare "per forza" se vuoi usare una classe astratta come interfaccia per il prodotto di una factory. poi puoi chiaramente esplicitare la
situazione ed automatizzare la distruzione usando qualcosa come auto_ptr o shared_ptr, come hai giustamente ricordato e mi pare di aver convenuto con te sull'opportunità di farlo;

rimane il fatto che l'oggetto sullo heap non verrà distrutto dalla classe che lo ha creato e mi pare che proprio questo sembrava dover essere "scandaloso" a prescindere da tutto.

l'alternativa è usare come interfaccia una classe non astratta (i metodi della quale ovviamente sarebbero implementati per non fare nulla, trattandosi dela "paradosso" di una istanza di un'interfaccia, nel caso venisse creato un oggetto di tale tipo) e ritornare l'oggeto del tipo concreto prodotto per valore, ma cadrebbe lo spunto iniziale visto che l'oggetto di interesse non sarebbe più sullo heap. (la discussione concettuale si sposterebbe altrove, immagino sia molto criticabile anche il fatto di poter creare un'istanza di un'interfaccia, anche se sarebbe inutile e non verrebbe mai effettivamente fatto)

Tipo? Io conosco solo le Xerces che essendo un ibrido C/C++ ti ritornano puntatori che il più delle volte devi deallocarti.
Ma certamente non esiste una sola API che ti ritorna un riferimento e nella documentazione ti dice che quell'oggetto te lo devi deallocare.

http://doc.trolltech.com/4.6/qtextcodec.html#codecForMib

per es questo metodo ritorna un puntatore ad un oggetto e la documentazione non ti dice nulla sul fatto se diventa roba tua, l'oggetto, o se sarà la lib. a gestirne il ciclo di vita ...

poi, visto che si può supporre che by design avranno teso a minimizzare le probabilità di leak, e visto che non dicono esplcitamente di distruggerlo finito l'uso, si può optare per evitare la delete nel chiamante, ma in prima istanza rimane solo una ipotesi/deduzione, .. .una riga in più nella documentazione non faceva male ...

... tanto più che, senza nemmeno farlo apposta trovo nella documentazione della medesima classe uno snippet di esempio in cui al contrario un ulteriore oggetto restituito per indirizzo viene proprio deallocato dal chiamante quando non gli serve più :asd:

inoltre ben più comune è il caso "inverso" ma concettualmente simile, in cui passi un puntatore ad una api, puntatore ad un oggetto che hai creato nel chiamante o cmq altrove nel tuo codice "client" rispetto alla lib, ma che non avrai necessità o anche non dovrai permetterti di deallocare. situazione opposta alla factory ma che ripropone ancora un caso di oggetti sullo heap non distrutti dalla classe che li ha creati.

Se non puoi ci sono nel linguaggio le alternative per indicare tale circostanza, quindi perchè non usarle?
Se non viene fatto il più delle volte è perchè trovi una strutturazione molto alla C.

infatti ribadisco che non ho sostenuto nulla contro l'uso di auto_ptr et similia, ci deve essere stato anche un malinteso strada facendo


Sai un programma lo puoi sempre scrivere tutto nel main oppure strutturare ad oggetti applicando tutti i pattern di questo mondo. Il programma può funzionare benissimo in entrambi i casi.
Per questo si parla di best practices.

così mi metti un po' in imbarazzo ... qualunque risposta a questo punto sembrerà un accettare un invito alla polemica ... d'altra parte non ho voluto escluderlo perché avendo quotato tutti i punti poteva sembrare di volerne ignorare uno per "comodità" :stordita: ... cmq nessuna "animosità" , per carità :D ... e, per la cronaca, anch'io voto per strutturazione e best practices, come tutti immagino.


E questo che c'entra? Anche un oggetto ritornato per valore viene deallocato quando va fuori dallo scope nel chiamante e allora?

infatti, lo scandalo era sul non distruggere un oggetto sullo heap nella stessa classe che lo ha creato, il caso ben più "tranquillo" di un oggetto sullo stack direi che esula dal punto inizialmente in questione :sofico:


Infatti esiste da quel dì una classe chiamata shared_ptr, devi tornare un puntatore e non sai come viene usato? Torna uno shared_ptr e vedrai che risolverai molti dei tuoi problemi. Non a caso auto_ptr è deprecato.
ribadisco che nessuno ti ha "dato contro" sull'uso di auto_ptr/shared_ptr


Direi che questo c'entra poco con il discorso.
E' vero che tanti dicono male del singleton, ma in un programma ci sono delle variabili effettivamente globali, tipo le configurazioni.
Oppure pensa solo all'MVC se dei controller devono condividere lo stesso Model a chi chiederanno l'istanza del Model? Se non lo fai con un factory "singleton" io non ho molte altre alternative, se non quella che gli venga passato dal chiamante, ma non necessariamente il chiamante è in un contesto tale da avere un riferimento al Model richiesto.
Qualcuno ha altre alternative?

guarda che sul non abolire universalmente il singleton pattern con me sfondi una porta apreta :D



Sei te che l'hai presa come "regola assoluta", qui stiamo parlando di "best practice".
Sennò perchè non consigliare di scrivere tutto nel main così ci si evitano direttamente chiamate a funzioni o metodi con tutti gli eventuali problemi della gestione del ritorno?

best practice appunto, che non sempre però è possibile applicare. ti ricordo che inizialmente la best practice di cui si parlava era semplicemente di deallocare un oggetto sullo heap nel codice della stessa classe che lo ha creato e che a più d'uno è sembrato "scandaloso" ipotizzare situazioni in cui questo non avvenga, mentre continua sembrarmi chiaro che ci siano casi in cui è inevitabile



:confused: E questo che c'entra? Non ci sono certo problemi nell'implementare interfacce in C++, nè tanto meno factory.

no nessun problema radicale per carità, però se come interfaccia usi una classe astratta ed il prodotto lo vuoi ritornare come il tipo intefaccia, allora lo devi creare sullo heape ritornare per indirizzo ... ma non lo puoi deallocare nella stessa classe che lo ha creato (non molto sensatamente almeno essendo una factory) ... siamo partiti più o meno da qui no ? :sofico:

Wing_Zero
09-08-2010, 08:48
no nessun problema radicale per carità, però se come interfaccia usi una classe astratta ed il prodotto lo vuoi ritornare come il tipo intefaccia, allora lo devi creare sullo heape ritornare per indirizzo ... ma non lo puoi deallocare nella stessa classe che lo ha creato (non molto sensatamente almeno essendo una factory) ... siamo partiti più o meno da qui no ? :sofico:

Ecco, appunto. esattamente quello che intendevo.
Penso sia troppo palese per non concordare su questo punto.

cionci
09-08-2010, 11:08
chi dealloca l'oggetto?
il chiamante quando sa che non gli serve più.
E chi ti ha detto che non serva più a chi te lo ha ritornato ?
Chi dealloca il puntatore?
Scusa, ma come si dealloca un puntatore? al massimo lo metti =0.
Avevo frainteso il tuo discorso su object come puntatore, credevo ti riferissi ad Object.

Comunque riprendendo l'esempio che hai fatto:

Object &Classe::object()
{
Object* object = new object();
return object;
}
Forse return *object ?
O Object * come tipo di ritorno ?

Sui Factory object o Factory method... Tipicamente mi piace che non ritornino per riferimento o per puntatore. Dal punto di vista prestazionale certo non è la scelta migliore ed in altri casi non è nemmeno la scelta migliore dal punto di vista architetturale.

Nel caso in cui si ritorni un puntatore, allora è implicito per la natura dei pattern stessi che perdano il controllo su ciò che producono.

Solitamente se si ritorna per riferimento, il chiamante ha in mano un alias ad un altro oggetto, quindi implicitamente si accetta che l'oggetto abbia vita oltre alla distruzione dell'alias. Mi sembra francamente la soluzione peggiore per uno dei pattern sopra, a meno che il Factory object (farlo con un Factory method sarebbe assurdo) non resti il proprietario dell'oggetto e ne sia responsabile anche della deallocazione.

Giusto per fare un esempio, generalizzando:
- se ritorno un puntatore ad una macchina è come se mi dicessero: "la macchina è tua e ti regalo anche le chiavi"
- se invece ritorno un riferimento ad una macchina è come se mi dicessero: "ti presto la macchina, ma resta mia, usala con le chiavi di riserva, ma le altre ce l'ho io, quindi la posso usare anche io"

MenageZero
09-08-2010, 13:28
E chi ti ha detto che non serva più a chi te lo ha ritornato ?

Avevo frainteso il tuo discorso su object come puntatore, credevo ti riferissi ad Object.

Comunque riprendendo l'esempio che hai fatto:

Object &Classe::object()
{
Object* object = new object();
return object;
}
Forse return *object ?
O Object * come tipo di ritorno ?

Sui Factory object o Factory method... Tipicamente mi piace che non ritornino per riferimento o per puntatore. Dal punto di vista prestazionale certo non è la scelta migliore ed in altri casi non è nemmeno la scelta migliore dal punto di vista architetturale.

Nel caso in cui si ritorni un puntatore, allora è implicito per la natura dei pattern stessi che perdano il controllo su ciò che producono.

Solitamente se si ritorna per riferimento, il chiamante ha in mano un alias ad un altro oggetto, quindi implicitamente si accetta che l'oggetto abbia vita oltre alla distruzione dell'alias. Mi sembra francamente la soluzione peggiore per uno dei pattern sopra, a meno che il Factory object (farlo con un Factory method sarebbe assurdo) non resti il proprietario dell'oggetto e ne sia responsabile anche della deallocazione.

Giusto per fare un esempio, generalizzando:
- se ritorno un puntatore ad una macchina è come se mi dicessero: "la macchina è tua e ti regalo anche le chiavi"
- se invece ritorno un riferimento ad una macchina è come se mi dicessero: "ti presto la macchina, ma resta mia, usala con le chiavi di riserva, ma le altre ce l'ho io, quindi la posso usare anche io"

mi permetto di aggiungere qualche considerazione sull'esempio di wing e sulle tue osservazioni visto che mi ero "intromesso" nella discussione proprio attarverso l'esempio della factory

secondo me l' "&" è una svista, azzarderi che intendesse Qbject* come tipo di ritorno visto che si parlava di lasciare al chiamante la distruzione di object

in uno scenario del genere penso si posa considerare l'assunzione che chi ritorna Object* perda ogni riferimento e controllo su object, visto che appunto il chiamato sarebbe una classe Factory; come tra l'altro tu stesso puntualizzi nel discutere l'opzione con Object* come tipo di ritorno, no ?

forse sbaglio ma una factory che rimane proprietario delle istanze prodotto mi parrebbe ben poco factory semanticamente :D ,
un chiamato che ritorna un puntatore o riferimento ed è proprietario dell'oggetto(o che cmq semanticamente include l'assunzione che il chiamante non deve deallocare l'oggetto) mi da più l'dea di situazione da pattern tipo registry/repository o semplicemente "mappa", in generale ... cmq non factory .. che ne pensi ?

edit:
volevo anche chiedere un parere (anche a chiunque altro volesse dire al sua ovviamente)su una cosa che avevo accennato in un altro post,

ovvero usare come interfaccia una classe concreta (con implementazione dei relativi metodi che ovviamente non fa nulla, e che magari ha un metodo - per es un cosa come bool isNull() - per distinguere se un oggeto di quel tipo è veramente una implementazione dell'interfaccia o una inutile istanza dell' "interfaccia" stessa, anche se in tale scenario probabilmente poco ortdosso ovviamente l'assunzione sarebbe che nessuno creerebbe mai un'istanza dell' "interfaccia")

tale approccio potrebbe servire , in una implementazione di factory, per poter ritornare le istanze prodotto effettivamente per valore aggirando totalmente la questione di dover avere un oggetto sullo heap che non potrà essere distrutto dalla stessa classe/livello di astrazione che lo ha creato.

ovviamente il tutto sposterebbe i problemi concettuali sul fatto di ritrovarsi un' "intefaccia" istanziabile, anche se non ci sarebbe motivo di crearne istanze dirette. magari è riternuto molto peggio che il factory method ritornante un puntatore (magari wrappato con auto_ptr o altro) "comprensivo" di ownership sull'oggetto puntato :D

cionci
09-08-2010, 15:08
mi permetto di aggiungere qualche considerazione sull'esempio di wing e sulle tue osservazioni visto che mi ero "intromesso" nella discussione proprio attarverso l'esempio della factory

secondo me l' "&" è una svista, azzarderi che intendesse Qbject* come tipo di ritorno visto che si parlava di lasciare al chiamante la distruzione di object

Io sinceramente l'avevo letto così nella prima interpretazione:

Object &Classe::object()
{
Object* object = new object();
return *object;
}

forse sbaglio ma una factory che rimane proprietario delle istanze prodotto mi parrebbe ben poco factory semanticamente :D
Imho di fatto diventa una via di mezzo fra factory e repository.
Per la parte creazionale resta comunque un factory, perché è l'unico che può creare un oggetto di un certo tipo.

edit:
volevo anche chiedere un parere (anche a chiunque altro volesse dire al sua ovviamente)su una cosa che avevo accennato in un altro post,

ovvero usare come interfaccia una classe concreta (con implementazione dei relativi metodi che ovviamente non fa nulla, e che magari ha un metodo - per es un cosa come bool isNull() - per distinguere se un oggeto di quel tipo è veramente una implementazione dell'interfaccia o una inutile istanza dell' "interfaccia" stessa, anche se in tale scenario probabilmente poco ortdosso ovviamente l'assunzione sarebbe che nessuno creerebbe mai un'istanza dell' "interfaccia")
Non è un Null Object pattern ?

Wing_Zero
09-08-2010, 19:48
E chi ti ha detto che non serva più a chi te lo ha ritornato ?

Avevo frainteso il tuo discorso su object come puntatore, credevo ti riferissi ad Object.

Comunque riprendendo l'esempio che hai fatto:

Object &Classe::object()
{
Object* object = new object();
return object;
}
Forse return *object ?
O Object * come tipo di ritorno ?

Sui Factory object o Factory method... Tipicamente mi piace che non ritornino per riferimento o per puntatore. Dal punto di vista prestazionale certo non è la scelta migliore ed in altri casi non è nemmeno la scelta migliore dal punto di vista architetturale.

Nel caso in cui si ritorni un puntatore, allora è implicito per la natura dei pattern stessi che perdano il controllo su ciò che producono.

Solitamente se si ritorna per riferimento, il chiamante ha in mano un alias ad un altro oggetto, quindi implicitamente si accetta che l'oggetto abbia vita oltre alla distruzione dell'alias. Mi sembra francamente la soluzione peggiore per uno dei pattern sopra, a meno che il Factory object (farlo con un Factory method sarebbe assurdo) non resti il proprietario dell'oggetto e ne sia responsabile anche della deallocazione.

Giusto per fare un esempio, generalizzando:
- se ritorno un puntatore ad una macchina è come se mi dicessero: "la macchina è tua e ti regalo anche le chiavi"
- se invece ritorno un riferimento ad una macchina è come se mi dicessero: "ti presto la macchina, ma resta mia, usala con le chiavi di riserva, ma le altre ce l'ho io, quindi la posso usare anche io"

Allora, apparte la svista dell'&, l'esempio pratico che ho portato io era la factory.
Come ha appunto fatto notare managezero in un caso del genere la factory ( prendendo l'esempio della macchina) "ti regala l'unica copia di chiavi che esiste".

In questo caso risulta palese che l'oggetto debba essere distrutto fuori dalla classe stessa...

MenageZero
09-08-2010, 20:07
Non è un Null Object pattern ?

in effetti magari l'idea l'avevo presa da qualche parte, non ho esattamente una memoria di ferro con i nomi, specie se sono di qualcosa che non applico spesso o recentemente :D

da quello che vedo con una velocissima (qundi imprecisissima) google-ata probabilmente una classe fatta in quel modo può esserevi assimilata, solo a prima vista non ho notato riferimenti a farne un uso da interfaccia :boh:

cionci
10-08-2010, 01:08
Il Null Pattern comunque può essere usato anche con l'interfaccia, si estrae l'interfaccia (classe astratta) dal Null Pattern che coinciderà con quella delle implementazione concrete.
Quindi il Null Pattern deriverà dalla classe base astratta così come le altre implementazioni.
Allora, apparte la svista dell'&, l'esempio pratico che ho portato io era la factory.
Come ha appunto fatto notare managezero in un caso del genere la factory ( prendendo l'esempio della macchina) "ti regala l'unica copia di chiavi che esiste".

In questo caso risulta palese che l'oggetto debba essere distrutto fuori dalla classe stessa...
Certo, ma io partivo dal fatto che tu ritornassi un Object &, non un Object *...

Resta il fatto che se per una factory la cosa è palese, non è sempre palese in altri casi. In questo caso non trovo necessario dover ritornare un puntatore. Meglio un riferimento ma non allocato dinamicamente.