Domanda

In un progetto C++ che utilizza puntatori intelligenti, come boost::shared_ptr, qual è una buona filosofia di progettazione per quanto riguarda l'uso di "this"?

Considera che:

  • È pericoloso archiviare il puntatore grezzo contenuto in qualsiasi puntatore intelligente per un uso successivo.Hai rinunciato al controllo dell'eliminazione degli oggetti e ti fidi del puntatore intelligente per farlo al momento giusto.

  • I membri della classe non statici utilizzano intrinsecamente a this puntatore.È un puntatore grezzo e non può essere modificato.

Se mai dovessi conservare this in un'altra variabile o passarlo a un'altra funzione che potrebbe potenzialmente memorizzarlo per dopo o associarlo in un callback, sto creando bug che vengono introdotti quando qualcuno decide di creare un puntatore condiviso alla mia classe.

Detto questo, quando mai sarà appropriato per me utilizzare esplicitamente a this puntatore? Esistono paradigmi di progettazione che possono prevenire i bug correlati a questo?

È stato utile?

Soluzione

Anche se non ho una risposta generale o qualche linguaggio, v'è boost::enable_shared_from_this . Esso consente di ottenere uno shared_ptr gestione di un oggetto che è già gestito da shared_ptr. Dato che in una funzione membro non si ha riferimento a coloro che gestiscono shared_ptr di, enable_shared_ptr non permetterà di ottenere un'istanza shared_ptr e passa che, quando è necessario passare il puntatore this.

Ma questo non risolverà il problema di passare this dall'interno del costruttore, poiché a quel tempo, non shared_ptr sta gestendo l'oggetto ancora.

Altri suggerimenti

Domanda sbagliata

In un progetto C++ che utilizza puntatori intelligenti

In realtà il problema non ha nulla a che fare con i puntatori intelligenti.Si tratta solo di proprietà.

I puntatori intelligenti sono solo strumenti

Non cambiano nulla rispetto al concetto di proprietà, specialmente. la necessità di avere una proprietà ben definita nel tuo programma, il fatto che la proprietà può essere trasferita volontariamente, ma non può essere acquisita da un cliente.

È necessario comprendere che i puntatori intelligenti (anche i lucchetti e altri oggetti RAII) rappresentano un valore e una relazione WRT questo valore allo stesso tempo.UN shared_ptr è un riferimento a un oggetto e stabilisce una relazione:l'oggetto non deve essere distrutto prima di ciò shared_ptr, e quando questo shared_ptr viene distrutto, se è l'ultimo a creare un alias per questo oggetto, l'oggetto deve essere distrutto immediatamente.(unique_ptr può essere visto come un caso speciale di shared_ptr dove per definizione non c'è aliasing, quindi il unique_ptr è sempre l'ultimo a creare un alias per un oggetto.)

Perché dovresti usare i puntatori intelligenti

Si consiglia di utilizzare puntatori intelligenti perché esprimono molto solo con dichiarazioni di variabili e funzioni.

I puntatori intelligenti possono solo esprimere un design ben definito, non eliminano la necessità di definire la proprietà.Al contrario, la garbage collection elimina la necessità di definire chi è responsabile della deallocazione della memoria.(Ma non elimina la necessità di definire chi è responsabile della pulizia di altre risorse.)

Anche nei linguaggi di garbage collection non puramente funzionali, è necessario rendere chiara la proprietà:non vuoi sovrascrivere il valore di un oggetto se altri componenti necessitano ancora del vecchio valore.Ciò è particolarmente vero in Java, dove il concetto di proprietà della struttura dati mutabile è estremamente importante nei programmi threaded.

Che dire dei puntatori grezzi?

L'uso di un puntatore grezzo non significa che non vi sia proprietà.Semplicemente non è descritto da una dichiarazione di variabile.Può essere descritto nei commenti, nei documenti di progettazione, ecc.

Ecco perché molti programmatori C++ ritengono che l'uso di puntatori grezzi invece del puntatore intelligente adeguato lo sia inferiore:perché meno espressivo (ho evitato di proposito i termini "buono" e "cattivo").Credo che il kernel Linux sarebbe più leggibile con alcuni oggetti C++ per esprimere le relazioni.

Puoi implementare un progetto specifico con o senza puntatori intelligenti.L'implementazione che utilizza il puntatore intelligente in modo appropriato sarà considerata superiore da molti programmatori C++.

La tua vera domanda

In un progetto C++, qual è una buona filosofia di progettazione riguardo all'uso di "questo"?

E' terribilmente vago.

È pericoloso archiviare il puntatore grezzo per un uso successivo.

Perché è necessario un puntatore per un uso successivo?

Hai rinunciato al controllo dell'eliminazione degli oggetti e hai fiducia che il componente responsabile lo faccia al momento giusto.

In effetti, alcuni componenti sono responsabili della durata della variabile.Non puoi assumerti la responsabilità:deve essere trasferito.

Se mai lo memorizzassi in un'altra variabile o lo passassi a un'altra funzione che potrebbe potenzialmente memorizzarlo per dopo o associarlo in un callback, sto creando bug che vengono introdotti quando qualcuno decide di utilizzare la mia classe.

Ovviamente, poiché il chiamante non viene informato che la funzione nasconderà un puntatore e lo utilizzerà in seguito senza il controllo del chiamante, si stanno creando dei bug.

La soluzione è ovviamente:

  • trasferire la responsabilità di gestire la durata dell'oggetto alla funzione
  • assicurarsi che il puntatore venga salvato e utilizzato solo sotto il controllo del chiamante

Solo nel primo caso potresti ritrovarti con un puntatore intelligente nell'implementazione della classe.

La fonte del tuo problema

Penso che il tuo problema sia che stai cercando di complicare le cose usando puntatori intelligenti.I puntatori intelligenti sono strumenti per rendere le cose più facili, non più difficili.Se i puntatori intelligenti complicano le tue specifiche, ripensale in termini di cose più semplici.

Non provare a introdurre i puntatori intelligenti come soluzione prima di avere un problema.

Introduci puntatori intelligenti solo per risolvere un problema specifico e ben definito.Poiché non descrivi un problema specifico e ben definito, non è possibile discutere una soluzione specifica (che coinvolgono puntatori intelligenti o meno).

Un esempio di utilizzo corretto è return *this; in funzioni come operatore ++ () e operator << ().

Quando si utilizza una classe puntatore intelligente, hai ragione che è pericoloso esporre direttamente "this". Ci sono alcune classi di puntatore relative al boost::shared_ptr<T> che possono essere utili:

  • boost::enable_shared_from_this<T>
    • Consente di avere un oggetto restituisce un puntatore condiviso per sé che utilizza gli stessi dati di conteggio di riferimento come un puntatore condiviso esistente all'oggetto
  • boost::weak_ptr<T>
    • lavora mano nella mano con i puntatori condivise, ma non tenere un riferimento all'oggetto. Se tutti i puntatori condivisi andare via e l'oggetto viene rilasciato, un puntatore debole, sarà in grado di dire che l'oggetto non esiste più e si tornerà NULL invece di un puntatore alla memoria non valida. È possibile utilizzare i puntatori deboli per ottenere puntatori condivisi per un oggetto di riferimento-contato valido.

Nessuno di questi è infallibile, naturalmente, ma sarà almeno rendere il codice più stabile e sicuro, fornendo accesso appropriato e il conteggio di riferimento per gli oggetti.

Se avete bisogno di usare this, basta usare esplicitamente. puntatori intelligenti avvolgono solo i puntatori degli oggetti di loro proprietà - o esclusivamente (unique_ptr) o in modo condiviso (shared_ptr).

personalmente mi piace utilizzare il questo puntatore quando si accede a variabili membro della classe. Ad esempio:

void foo::bar ()
{
    this->some_var += 7;
}

E 'solo una domanda innocua di stile. Alcune persone, come esso, somepeople non lo fanno.

Ma con il questo puntatore per qualsiasi altra cosa è probabile che a causare problemi. Se avete veramente bisogno di fare le cose di fantasia con esso, si dovrebbe davvero rivedere il vostro disegno. Una volta ho visto un po 'di codice che, nel costruttore di una classe, è assegnato il questo puntatore a un altro puntatore memorizzato da qualche altra parte! Questo è solo folle, e non posso mai pensare a una ragione per farlo. L'intero codice era una grande confusione, tra l'altro.

Puoi dirci esattamente cosa vuoi fare con il puntatore?

Un'altra opzione utilizza puntatori intelligenti intrusivi, e avendo cura di conteggio di riferimento all'interno dell'oggetto stesso, non i puntatori. Questo richiede un po 'più di lavoro, ma in realtà è più efficiente e facile da controllare.

Un altro motivo per passare intorno a questo è che se si vuole mantenere un registro centrale di tutti gli oggetti. Nel costruttore, un oggetto chiama un metodo statico del registro con questo. Il suo utile per vari pubblicazione / sottoscrizione meccanismi, o quando non si desidera che il Registro di sistema per bisogno di conoscenza di ciò che oggetti / classi sono nel sistema.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top