2010-09-25 2 views
8

Mi è stato recentemente introdotto l'esistenza di auto_ptr e shared_ptr e ho una domanda piuttosto semplice/ingenua.Pointers vs auto_ptr vs shared_ptr

Cerco di implementare una struttura dati e ho bisogno di indicare i figli di un numero Node che (sono più di 1 e il suo) numero può cambiare. Quale è la migliore alternativa e perché:

class Node 
{ 
    public: 
     // ... 
     Node *children; 

    private: 
     //... 
} 

class Node 
{ 
    public: 
     // ... 
     share_ptr<Node> children; 

    private: 
     //... 
} 

Non sono sicuro, ma penso che auto_ptr non funziona per gli array. Non sono, inoltre, sicuro se dovrei usare i doppi puntatori. Grazie per qualsiasi aiuto.

+0

auto_ptr è deprecato in C++ 11 e dovrebbe essere evitato, se possibile, in codice nella vecchia versione C++ pure. – Nikko

+0

'auto_ptr' è deprecato perché è inutilmente difficile da usare correttamente. Usa 'unique_ptr' invece che è fondamentalmente lo stesso di 'auto_ptr', solo che funziona correttamente e supporta anche gli array. È disponibile dal C++ 11. – nwp

+0

anche dare un'occhiata a http://stackoverflow.com/questions/3987521/how-bad-is-to-use-void-pointer-in-stdvector-declaration – fizzbuzz

risposta

7

Hai ragione che auto_ptr non funziona per gli array. Quando distrugge l'oggetto che possiede, utilizza delete object;, quindi se hai usato new objects[whatever];, otterrai un comportamento indefinito. Forse un po 'più sottilmente, auto_ptr non soddisfa i requisiti di "Copiabile" (poiché lo standard definisce il termine) quindi non è possibile creare un contenitore (vettore, deque, elenco, ecc.) Di auto_ptr.

A shared_ptr è anche per un singolo oggetto. È per una situazione in cui hai condiviso la proprietà e devi eliminare l'oggetto solo quando tutti i i proprietari escono dal campo di applicazione. A meno che non ci sia qualcosa che non ci hai detto, ci sono buone probabilità che non si adattino molto bene alle tue esigenze.

Si potrebbe voler guardare ancora un'altra classe che potrebbe essere nuova per te: Boost ptr_vector. Almeno in base a ciò che hai detto, sembra adattarsi alle tue esigenze meglio di quanto farebbe lo auto_ptr o lo shared_ptr.

+0

Grazie! Quindi ho capito che dovrei andare con 'ptr_vector' o' Node * children'. Non potrei usare 'auto_ptr' per puntare a un' std :: vector' di 'Node's? Inoltre, il 'Node * children' è giusto o preferisco' Nodo ** children'? Sono un po 'confuso. Ci scusiamo per aver fatto troppe domande qui. –

+0

@myle: Senza sapere di più su quello che stai facendo, è difficile dirlo con certezza. Fondamentalmente, un 'Nodo *' ti darà un punto per un numero arbitrario di Nodi, quindi quei nodi saranno fondamentalmente parte del loro genitore, non solo collegati ad esso. Un 'Nodo **' ti permetterà di avere una matrice dinamica di puntatori ai nodi, ma dovrai gestire tu stesso l'array dinamico. –

3

Ho utilizzato con successo std::vector<std::shared_ptr<Node> > children in una situazione simile.

Il vantaggio principale dell'utilizzo di un vettore di shared_ptrs piuttosto che di un array è che tutta la gestione delle risorse è gestita per voi. Ciò è particolarmente utile in due situazioni:
1) Quando il vettore non è più incluso, chiama automaticamente l'eliminazione su tutto il suo contenuto. In questo caso, il conteggio dei riferimenti del nodo figlio diminuirà di 1 e se nient'altro lo fa riferimento, l'eliminazione verrà richiamata sull'oggetto.
2) Se si fa riferimento al nodo altrove, non vi è alcun rischio di rimanere con un puntatore pendente su un oggetto eliminato. L'oggetto verrà cancellato solo quando non ci sono più riferimenti ad esso.

A meno che non si desideri un comportamento sostanzialmente più complicato (forse c'è un motivo per cui è necessario un array), suggerirei che questo potrebbe essere un buon approccio per te.

Una semplice implementazione dell'idea:

class Node { 
private: 
    T contents; 
    std::vector<std::shared_ptr<Node> > children; 

public: 
    Node(T value) : contents(value) {}; 

    void add_child(T value) { 
     auto p = std::make_shared<Node>(value); 
     children.push_back(p); 
    } 

    std::shared_ptr<Node> get_child(size_t index) { 
     // Returning a shared pointer ensures the node isn't deleted 
     // while it is still in use. 
     return children.at(index); 
    } 

    void remove_child(size_t index) { 
     // The whole branch will be destroyed automatically. 
     // If part of the tree is still needed (eg. for undo), the 
     // shared pointer will ensure it is not destroyed. 
     children.erase(children.begin() + index); 
    } 

}; 
+0

Inoltre, si noti che è possibile fornire il proprio distruttore a shared_ptr in modo da poterne usare uno per gestire un array chiamando delete [] invece di plain delete. – QuesterZen

+0

Il modo più semplice (ma leggermente brutto) di aggiungere un deleter personalizzato per gli array è utilizzare una funzione lambda, che viene passata al costruttore come argomento aggiuntivo. Es .: 'std :: shared_ptr sp (new T [n], [] (T * p) {delete [] p;})'. Per unique_ptrs esiste una versione speciale prevista per questo: 'std :: unique_ptr su (nuovo T [n])' che chiama delete [] invece di delete. – QuesterZen

1

auto_ptr è deprecato in favore di std::unique_ptr e btw. std::unique_ptr funziona per gli array. Hai solo bisogno del supporto per C++ 11. E ci sono già molte risorse sui puntatori intelligenti e spostare la semantica là fuori. La differenza principale tra auto_ptr e unique_ptr è che auto_ptr sposta quando si chiama il costruttore di copie e unique_ptr vieta il costruttore di copie, ma consente un move quando si chiama il costruttore di movimento. Quindi hai bisogno del supporto C++ 11 con la semantica del movimento.

1

Stroustrup discute la domanda "Che cos'è un auto_ptr e perché non c'è un auto_array" e conclude che non c'è bisogno di quest'ultimo poiché la funzionalità desiderata può essere realizzata con un vettore.

http://www.stroustrup.com/bs_faq2.html#auto_ptr