2010-09-25 20 views
8

Me han presentado recientemente la existencia de auto_ptr y shared_ptr y tengo una pregunta bastante simple/ingenua.Punteros vs auto_ptr vs shared_ptr

Intento implementar una estructura de datos y necesito señalar a los hijos de un Node que (son más de 1 y su número) pueden cambiar. ¿Cuál es la mejor alternativa y por qué:

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

    private: 
     //... 
} 

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

    private: 
     //... 
} 

No estoy seguro, pero creo que auto_ptr no funciona para matrices. Tampoco estoy seguro de si debería usar dobles punteros. Gracias por cualquier ayuda.

+0

auto_ptr está en desuso en C++ 11 y se debe evitar si es posible en el código en la versión más antigua C++ también. – Nikko

+0

'auto_ptr' está en desuso porque es innecesariamente difícil de usar correctamente. Utilice 'unique_ptr' en su lugar, que es básicamente lo mismo que' auto_ptr', solo que funciona correctamente y también admite matrices. Está disponible desde C++ 11. – nwp

+0

también eche un vistazo a http://stackoverflow.com/questions/3987521/how-bad-is-to-use-void-pointer-in-stdvector-declaration – fizzbuzz

Respuesta

7

Tiene razón en que auto_ptr no funciona para las matrices. Cuando destruye el objeto que posee, usa delete object;, por lo que si usaste new objects[whatever];, obtendrás un comportamiento indefinido. Tal vez un poco más sutilmente, auto_ptr no se ajusta a los requisitos de "Copyable" (como el estándar define el término) por lo que no puede crear un contenedor (vector, deque, lista, etc.) de auto_ptr tampoco.

A shared_ptr es para un solo objeto también. Es para una situación en la que ha compartido la propiedad y necesita eliminar el objeto solo cuando todos los propietarios quedan fuera del alcance. A menos que ocurra algo de lo que no nos haya hablado, las posibilidades son bastante buenas de que tampoco se ajuste a sus necesidades.

Es posible que desee ver otra clase que puede ser nueva para usted: Boost ptr_vector. Al menos en base a lo que ha dicho, parece ajustarse mejor a sus requisitos que auto_ptr o shared_ptr.

+0

¡Gracias! Así que entiendo que debería ir con 'ptr_vector' o' Node * children'. ¿No podría usar 'auto_ptr' para apuntar a un' std :: vector' de 'Node's? Además, ¿es correcto el 'Node * children' o preferiría' Node ** children'? Estoy un poco confundido. Perdón por hacer demasiadas preguntas aquí. –

+0

@myle: Sin saber más sobre lo que estás haciendo, es difícil decirlo con certeza. Básicamente, un 'Nodo *' le dará un punto a una cantidad arbitraria de Nodos, por lo que esos nodos básicamente serán parte de sus padres, no solo vinculados a él. Un 'Nodo **' le permitirá tener una matriz dinámica de punteros a nodos, pero usted tendrá que administrar la matriz dinámica usted mismo. –

3

He usado std::vector<std::shared_ptr<Node> > children con éxito en una situación similar.

La principal ventaja de utilizar un vector de shared_ptrs en lugar de una matriz es que se maneja toda la administración de recursos para usted. Esto es especialmente útil en dos situaciones:
1) Cuando el vector ya no está en el alcance, automáticamente llama a eliminar en todos sus contenidos. En este caso, el recuento de referencia del nodo secundario se reducirá en 1 y si nada más hace referencia a él, se invocará eliminar en el objeto.
2) Si hace referencia al Nodo en otro lugar, no hay riesgo de quedarse con un puntero colgando a un objeto eliminado. El objeto solo se eliminará cuando ya no haya más referencias al mismo.

A menos que desee un comportamiento que sea sustancialmente más complicado (quizás haya una razón por la cual una matriz es necesaria), le sugiero que este podría ser un buen enfoque para usted.

Una simple aplicación de la 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

Además, tenga en cuenta que puede proporcionar su propio destructor a shared_ptr para que pueda usar uno para administrar una matriz llamando a delete [] en lugar de plain dele. – QuesterZen

+0

La manera más simple (pero ligeramente fea) de agregar un eliminador personalizado para matrices es usar una función lambda, que se pasa al constructor como un argumento adicional. Ejemplo: 'std :: shared_ptr sp (nuevo T [n], [] (T * p) {eliminar [] p;})'. Para unique_ptrs hay una versión especial provista para esto: 'std :: unique_ptr up (new T [n])' que llama a delete [] en lugar de delete. – QuesterZen

1

auto_ptr está en desuso en favor de std::unique_ptr y por cierto. std::unique_ptr funciona para arreglos. Solo necesitas compatibilidad con C++ 11. Y ya hay muchos recursos sobre punteros inteligentes y semántica de movimientos. La principal diferencia entre auto_ptr y unique_ptr es que auto_ptr hace un movimiento cuando se llama al constructor de copia y unique_ptr prohíbe el constructor de copia, pero permite una move al llamar al constructor de movimiento. Por lo tanto, necesita compatibilidad con C++ 11 con semántica de movimiento.

1

Stroustrup discute la pregunta de "¿Qué es un auto_ptr y por qué no hay un auto_array" y concluye que no es necesario para este último ya que la funcionalidad deseada se puede lograr con un vector.

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

Cuestiones relacionadas