Domanda

Si prega supponiamo di avere una funzione che accetta un puntatore come un parametro. Questa funzione può generare un'eccezione, in quanto utilizza std::vector<>::push_back() per gestire il ciclo di vita di questo puntatore. Se dichiaro in questo modo:

void manage(T *ptr);

e chiamare in questo modo:

manage(new T());

se viene generata un'eccezione spingendo il puntatore nella std::vector<>, ho effettivamente ho una perdita di memoria, non ho io?

Sarebbe dichiarare la funzione in questo modo:

void manage(std::auto_ptr<T> ptr);

risolvere il mio problema?

mi aspetterei che per assegnare prima il std::auto_ptr sullo stack (cosa che credo che non potrebbe mai un'eccezione) e lasciarlo acquisire la proprietà sopra il puntatore. Cassetta di sicurezza.

Poi, all'interno della funzione, vorrei spingere il puntatore prima nel std::vector<>, che è anche sicuro: se non funziona, non verrà aggiunto il puntatore, ma il puntatore intelligente sarà ancora possedere il puntatore in modo che sarà distrutto . Se la spinta riesce, mi tolgo di proprietà del puntatore intelligente oltre che puntatore e ritorno:. Questo non può generare un'eccezione, così sarà sempre bene

Sono le mie teorie corrette?

- Modifica -

No, penso che non posso farlo. Facendo che richiederebbe di prendere un riferimento non const a rvalue (per togliere proprietà dal puntatore intelligente). Avrei dovuto scrivere

std::auto_ptr<T> ptr(new T());
manage(ptr);

Per conseguire tale obiettivo, qualcosa che è abbastanza inconvient nel mio caso. Sto scrivendo questo modo che io possa implementare RAII senza inquinare molto il codice. Facendo che cosa non aiuterebbe, quindi. Sarebbe cattura 22.

- Modifica 2 -

Tirare quello che ha detto Jason Orendorff laggiù qui per riferimento rapido da parte dei lettori, la soluzione definitiva sembra essere il seguente:

void manage(T *ptr)
{
    std::auto_ptr<T> autoPtr(ptr);
    vector.push_back(ptr);
    autoPtr.release();
}

Questo risolve il problema del riferimento non-const inutile rvalue.

Quando ho finito questa classe sto codifica vi posterò tornare qui nel caso qualcuno lo trova utile.

- Modifica 3 -

Ok, c'è stato un sacco di discussioni qui, e ci sono punti chiave che avrei dovuto chiarito prima. In generale, quando posto a StackOverflow, cerco di spiegare il motivo dietro le mie domande e, in generale, che è completamente inutile. Così questa volta ho pensato che dovrebbe andare dritto al punto. Si scopre che non ha funzionato abbastanza bene XD

Purtroppo il mio cervello è deadlocking ora, quindi penso che non voglio nemmeno essere in grado di spiegare in modo corretto quello che ho pensato prima per raggiungere i miei obiettivi. Sto cercando di trovare una buona soluzione per le operazioni atomiche e la scrittura di codice sicuro rispetto alle eccezioni che si inserisce un sacco di casi, ma in realtà, non riesco a gestire la cosa XD Penso che questo è il genere di cosa che posso padroneggiare solo con il tempo.

Sono davvero un nuovo programmatore C ++ e il mio obiettivo è lo sviluppo di videogiochi. Quando viene generata un'eccezione in un motore di gioco, è la fine dell'esecuzione. Il sistema liberare tutta la memoria per il mio processo, quindi in realtà non importa se uno o due puntatori perdite qua e là. Ora che sto sviluppando un'applicazione server, sto trovando difficile da affrontare con le eccezioni, perché un'eccezione non può mandare in crash il server; essa deve "mandare in crash la richiesta".

Cioè, "Bene, cliente, purtroppo gli sviluppatori hanno non prevedere questa condizione, quindi dovrete provare in un secondo momento (fino a qui, è fondamentalmente la stessa cosa con un motore di gioco, non c'è nulla di essere repared, solo che è isolato al contesto di solo una richiesta, e non l'intero processo). Ma niente panico, come tutto è stato lasciato in uno stato valido (qui è una delle differenze, . tuttavia, il processo non è terminato, in modo che il sistema operativo non può liberare le risorse per voi, inoltre, si deve prestare attenzione per annullare le operazioni fino ad ora, in modo che non si blocca completamente l'account di un utente, ad esempio , o anche un servizio completo fornito dal server). ".

Mi limito a codificare sempre di più e annotare i miei problemi in modo che posso scrivere una domanda meglio la prossima volta. Non ero preparato achiedere a questa ora, mi dispiace davvero.

Grazie mille per le vostre risposte, mi piace molto StackOverflow. E 'assolutamente incredibile quanto velocemente le mie domande trovano risposta e come illuminante le vostre risposte sono. Grazie.

È stato utile?

Soluzione

Credo abbastanza discussione è stata generata da giustificare l'ennesima risposta.

In primo luogo, per rispondere alla domanda effettiva, sì, è assolutamente appropriata (e addirittura necessario!) Per passare un argomento da puntatore intelligente quando si verifica il trasferimento di proprietà. Passando da un puntatore intelligente è un idioma comune per realizzare questo.

void manage(std::auto_ptr<T> t) {
    ...
}
...

// The reader of this code clearly sees ownership transfer.
std::auto_ptr<T> t(new T);
manage(t);

Ora, c'è una buona ragione per cui tutti i puntatori intelligenti hanno costruttori espliciti. Si consideri la seguente funzione (sostituire mentalmente std::auto_ptr con boost::shared_ptr se solletica la vostra fantasia):

void func(std::auto_ptr<Widget> w, const Gizmo& g) {
    ...
}

Se std::auto_ptr aveva un costruttore implicita, improvvisamente questo codice compilato:

func(new Widget(), gizmo);

Cosa c'è che non va? Prendendo direttamente dalla "Effective C ++", punto 17:

func(new Widget(), a_function_that_throws());

Perché in C ++ ordine di valutazione argomentazione non è definito, si può benissimo avere argomenti valutati nel seguente ordine: new Widget(), a_function_that_throws(), costruttore std::auto_ptr. Se una funzione genera, si dispone di una perdita.

Pertanto tutte le risorse che saranno rilasciati necessità per essere avvolti su di costruzione nel RAII classi prima essere passato in una funzione. Ciò significa che tutti puntatore intelligente deve essere costruito prima di passarli come argomento a una funzione. Facendo puntatori intelligenti essere copia-costruibile con un riferimento const o implicitamente-costruibile incoraggerebbe codice unsafe. costruzione esplicita fa rispettare il codice più sicuro.

Ora, perché non dovresti fare qualcosa di simile?

void manage(T *ptr)
{
    std::auto_ptr<T> autoPtr(ptr);
    vector.push_back(ptr);
    autoPtr.release();
}

Come già accennato, i modi di dire di interfaccia mi dicono che posso passare un puntatore che possiedo e ho arrivare a eliminarlo. Quindi, nulla mi impedisce di fare questo:

T item;
manage(&t);

// or
manage(&t_class_member);

Il che è disastrosa, naturalmente. Ma avresti detto "naturalmente so che cosa significa l'interfaccia, non avevo mai usato in quel modo". Tuttavia si consiglia di aggiungere un argomento in più per una funzione più tardi. O qualcuno (non è vero, o 3 anni più tardi) arriva questo codice, non vedano allo stesso modo come si fa.

  1. Questa ipotetica "qualcun altro" può solo vedere un colpo di testa, senza un commento e (giustamente) presumere la mancanza di trasferimento della proprietà.
  2. Possono vedere come questa funzione viene utilizzata in qualche altro codice e replicare l'utilizzo senza guardare l'intestazione.
  3. Possono utilizzare il codice completamento automatico per richiamare una funzione e non leggere il commento o la funzione e presumere la mancanza di trasferimento della proprietà.
  4. Possono scrivere una funzione che ti avvolge la funzione manage e tuttavia qualcun altro utilizzerà la funzione di involucro e salterà la documentazione della funzione originale.
  5. Si può cercare di "estendere" si codice in modo che tutti i vecchi compila codice (e diventa automaticamente non sicuri):

    void manage(T *t, const std::string tag = some_function_that_throws());
    

Come si può vedere, esplicita la costruzione di un puntatore intelligente rende molto più difficile scrivere codice non sicuro nei casi di cui sopra.

Quindi, io non consiglio di andare contro decenni di esperienza C ++ per rendere perceivably e API "più bello" "divertimento".

Il mio 2c.

Altri suggerimenti

Si potrebbe fare in questo modo, ma si devono ancora di pulizia in caso di eccezione non essere gettato, che sembra un po 'oneroso.

Se si usa qualcosa come boost :: shared_ptr (credo che qualcosa di simile è nelle biblioteche TR1, come pure - come esempio vedi di MS implementazione ) si potrebbe dimenticare di dover pulizia in caso di cose che vanno come previsto.

Per fare questo lavoro si avrebbe bisogno per rendere il vostro vettore accetta esempio boost::shared_ptr < T >, allora si sarebbe semplicemente lasciare l'istanza originaria essere ripulito e sarebbe lasciare esempio nel vettore vivo nel caso in cui tutto va bene. Nel caso le cose vanno male tutte le istanze boost::shared_ptr sarebbero stati distrutti e si sarebbe ancora finire con nessuna perdita.

Con i puntatori intelligenti si tratta di sceglierne uno che si adatta al compito, e in questo caso la proprietà condivisa (o semplice trasferimento di proprietà) sembra essere un obiettivo per cui ci sono i candidati migliori di std :: auto_ptr sulla maggior parte delle piattaforme.

In generale, utilizzando std::auto_ptr come un tipo di argomento di funzione dice in modo inequivocabile il chiamante che funzione assumere la proprietà dell'oggetto, e sarà responsabile per l'eliminazione di esso. Se la funzione si adatta a questa descrizione, quindi con tutti i mezzi utilizzare auto_ptr lì indipendentemente da qualsiasi altra ragione.

Sì, va bene. (vedi modifica sotto.) (JKP potrebbe vedere qualcosa che ho perso, ma non ti credo "devono ancora pulire in caso di un'eccezione essere gettato", perché, come dici tu, in questo caso l'auto_ptr cancellerà l'oggetto per voi.)

Ma penso che sarebbe meglio per nascondere i shenanigans auto_ptr dal chiamante:

void manage(T *t) {
    std::auto_ptr<T> p(t);  // to delete t in case push_back throws
    vec.push_back(t);
    p.release();
}

Modifica Inizialmente ho scritto "Sì, va bene", riferendosi al piano di manage(auto_ptr<T>) originale, ma ho provato e scoperto che non funziona. Il costruttore è auto_ptr<T>::auto_ptr(T *) explicit. Il compilatore non avrebbe lasciato che si scrive manage(new T) perché non può convertire implicitamente che puntatore ad un auto_ptr. manage(T *) è un'interfaccia più amichevole comunque!

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