Con C++ 11 por ahí, me preguntaba si hay un reemplazo de boost :: ptr_containers en C++ 11. Sé que puedo usar, p. a std::vector<std::unique_ptr<T> >
, pero no estoy seguro si esto es un reemplazo completo. ¿Cuál es la forma recomendada de manejar estos casos?contenedor stl con std :: unique_ptr's boost :: ptr_container
Respuesta
Realmente resuelven dos problemas similares pero diferentes.
Un contenedor de puntero es una forma de almacenar objetos en un contenedor que, simplemente, son punteros a la memoria asignada en lugar de valores. Hacen todo lo que está en su poder para ocultar ocultar el hecho de que son un contenedor de punteros. Esto significa:
- Las entradas en el contenedor no pueden ser NULAS.
- Los valores que obtiene de los iteradores y las funciones son referencias al tipo, no apunta al tipo.
- Trabajar con muchos algoritmos estándar puede ser ... difícil. Y por "complicado", quiero decir roto. Los contenedores de puntero tienen sus propios algoritmos incorporados.
Sin embargo, el hecho de que los contenedores puntero saben que son contenedores de punteros, que pueden ofrecer algunas nuevas funcionalidades:
- Una función
clone
miembro que realiza una copia profunda, a través del uso de un cierto concepto "Clonable" sobre el tipo de objeto. - La capacidad de un contenedor para liberar la propiedad de sus objetos (después de una copia superficial, por ejemplo).
- Funciones integradas para transferir la propiedad a otros contenedores.
Realmente son conceptos bastante diferentes. Hay muchas cosas que debería hacer manualmente que los contenedores de puntero pueden hacer automáticamente con funciones especializadas.
Si realmente necesita un contenedor de punteros, entonces puede utilizar contenedores de unique_ptr
. Pero si necesita almacenar un conjunto de objetos que asigna de manera aleatoria, y desea jugar juegos especiales con ellos que impliquen propiedad y cosas así, entonces los contenedores de puntero no son una mala idea.
Supongo que se podría decir que uno de ellos es explícitamente un contenedor de punteros, mientras que el otro es un contenedor destinado a objetos polimórficos ... – Mehrdad
Decidí escribir un programa corto que colocaba unos pocos objetos polimórficos en un contenedor (por puntero a montón) y luego usaba ese contenedor con un algoritmo std ::. Elegí std::remove_if
solo como un ejemplo.
Así es como lo haría con vector<unique_ptr<T>>
:
#include <vector>
#include <memory>
#include <iostream>
class Animal
{
public:
Animal() = default;
Animal(const Animal&) = delete;
Animal& operator=(const Animal&) = delete;
virtual ~Animal() = default;
virtual void speak() const = 0;
};
class Cat
: public Animal
{
public:
virtual void speak() const {std::cout << "Meow\n";}
virtual ~Cat() {std::cout << "destruct Cat\n";}
};
class Dog
: public Animal
{
public:
virtual void speak() const {std::cout << "Bark\n";}
virtual ~Dog() {std::cout << "destruct Dog\n";}
};
class Sheep
: public Animal
{
public:
virtual void speak() const {std::cout << "Baa\n";}
virtual ~Sheep() {std::cout << "destruct Sheep\n";}
};
int main()
{
typedef std::unique_ptr<Animal> Ptr;
std::vector<Ptr> v;
v.push_back(Ptr(new Cat));
v.push_back(Ptr(new Sheep));
v.push_back(Ptr(new Dog));
v.push_back(Ptr(new Sheep));
v.push_back(Ptr(new Cat));
v.push_back(Ptr(new Dog));
for (auto const& p : v)
p->speak();
std::cout << "Remove all sheep\n";
v.erase(
std::remove_if(v.begin(), v.end(),
[](Ptr& p)
{return dynamic_cast<Sheep*>(p.get());}),
v.end());
for (auto const& p : v)
p->speak();
}
Este salidas:
Meow
Baa
Bark
Baa
Meow
Bark
Remove all sheep
destruct Sheep
destruct Sheep
Meow
Bark
Meow
Bark
destruct Dog
destruct Cat
destruct Dog
destruct Cat
la que se ve bien para mí. Sin embargo, me encontré a traducir esta problemática ptr_vector
:
boost::ptr_vector<Animal> v;
v.push_back(new Cat);
v.push_back(new Sheep);
v.push_back(new Dog);
v.push_back(new Sheep);
v.push_back(new Cat);
v.push_back(new Dog);
for (auto const& p : v)
p.speak();
std::cout << "Remove all sheep\n";
v.erase(
std::remove_if(v.begin(), v.end(),
[](Animal& p)
{return dynamic_cast<Sheep*>(&p);}),
v.end());
for (auto const& p : v)
p.speak();
algorithm:1897:26: error: overload resolution selected deleted operator '='
*__first = _VSTD::move(*__i);
~~~~~~~~^~~~~~~~~~~~~~~~~~
test.cpp:75:9: note: in instantiation of function template specialization 'std::__1::remove_if<boost::void_ptr_iterator<std::__1::__wrap_iter<void
**>, Animal>, Sheep *(^)(Animal &)>' requested here
std::remove_if(v.begin(), v.end(),
^
test.cpp:12:13: note: candidate function has been explicitly deleted
Animal& operator=(const Animal&) = delete;
^
1 error generated.
El problema es una característica de boost::ptr_vector
: los iteradores no devuelven los punteros almacenados internamente. Devuelven los punteros desreferenciados.Y así, cuando el contenedor se usa con std::algorithms
, los algoritmos intentan copiar los objetos almacenados en lugar de los punteros almacenados en los objetos.
Si uno accidentalmente se olvida de hacer sus objetos polimórficos no copiable, y luego copiar la semántica se suministran de forma automática, lo que resulta en un error de tiempo de ejecución en lugar de un error de tiempo de compilación:
class Animal
{
public:
Animal() = default;
virtual ~Animal() = default;
virtual void speak() const = 0;
};
Que ahora se traduce en esta errónea salida:
Meow
Baa
Bark
Baa
Meow
Bark
Remove all sheep
destruct Cat
destruct Dog
Meow
Baa
Bark
Baa
destruct Cat
destruct Sheep
destruct Dog
destruct Sheep
Este error de tiempo de ejecución no puede ocurrir al utilizar vector<unique_ptr>
.
La impedancia no coincidente de almacenar contenedores de punteros pero presentar contenedores de referencias parece estar en desacuerdo con el uso seguro de los contenedores con algoritmos genéricos. De hecho, esta es la razón por la cual los ptr_containers vienen con versiones personalizadas de muchos de los algoritmos. La forma correcta de hacer este trabajo con ptr_containers es utilizar única esos algoritmos miembros:
v.erase_if([](Animal& p)
{return dynamic_cast<Sheep*>(&p);});
Si necesita un algoritmo de secuencia de la mutación no suministrado como miembro de los ptr_containers, no tener la tentación de llegar para aquellos en <algorithm>
, o aquellos algoritmos genéricos suministrados por terceros.
En resumen, el boost :: ptr_containers suplía una necesidad real cuando la única otra opción práctica era std::vector<boost::shared_ptr<T>>
. Sin embargo, ahora con std::vector<std::unique_ptr<T>>
, el argumento general se ha ido. Y parece que existen ventajas de seguridad y flexibilidad con la solución C++ 11. Si necesita "semántica de clonación", consideraría seriamente escribir su propio clone_ptr<T>
y usarlo con los contenedores estándar y los algoritmos.
La reutilización de std :: lib mantendrá sus opciones de contenedores más abiertas que la lib boost (por ejemplo, unordered_set/map, forward_list), y mantendrá sus opciones de std :: algorithms abiertas lo más amplias posible.
Dicho esto, si tiene un código depurado que ya utiliza el boost :: ptr_containers, no hay necesidad urgente de cambiarlo.
"el argumento general se ha ido" - Hoy, hice algunas pruebas con VS2013-Express y sorprendentemente obtengo mejores resultados de rendimiento con 'ptr_vector' que con' vector
Interesante, gracias por el informe. No tengo VS2013 para experimentar. Es 'sizeof (unique_ptr
Sí, sizeof (uq_ptr) == sizeof (T *) == 4./O2 es "maximizar para la velocidad" en VS. Veo una ganancia de velocidad del 50% con ptr_vector en el acceso de índice '[]'. –
- 1. ¿Cómo envolver la matriz dinámica en el contenedor STL/Boost?
- 2. impulso :: ptr_container y std :: vector <shared_ptr>
- 3. Inicialización contenedor de unique_ptrs de lista de inicialización falla con GCC 4.7
- 4. Reenviar declarar un contenedor STL?
- 5. Tipos de letra estándar para contenedor compatible con STL
- 6. vectores Boost versus vectores STL
- 7. Boost Python: contenedor polimórfico?
- 8. ¿Qt funciona bien con STL & Boost?
- 9. C++/STL: std :: transformar con paso dado?
- 10. ¿std :: string es parte del STL?
- 11. Valores devueltos de la función contenedor STL
- 12. boost :: filter_iterator - ¿cómo podría hacer eso con el STL?
- 13. Clases de contenedor STL con respaldo de disco?
- 14. std, boost u otra implementación generalizada de un contenedor de tabla hash con claves implícitas
- 15. boost :: ifind_first con std :: string objects
- 16. Implementación autónoma compatible con STL de std :: vector
- 17. Orden de destrucción de elementos de contenedor STL
- 18. ¿Hay alguna manera de acceder al contenedor subyacente de los adaptadores de contenedor STL?
- 19. ¿Cómo medir el consumo total de memoria del contenedor STL?
- 20. ¿Qué contenedor STL debo usar para un FIFO?
- 21. boost zip_iterator y std :: sort
- 22. Comparación de punto flotante en STL, BOOST
- 23. ¿es seguro asignar un contenedor STL?
- 24. Boost equivalente a std :: async()
- 25. una línea para comprobar si el contenedor STL está ordenado
- 26. Cómo usar std :: allocator en mi propia clase de contenedor
- 27. ¿Hay un algoritmo STL/boost para verificar que todos los elementos en un contenedor coinciden con un valor?
- 28. ¿Qué contenedor STL debe realizar la eliminación de elementos intermedios?
- 29. Examinar el siguiente elemento en el contenedor STL
- 30. C contenedor STL ++ y la construcción en el lugar
Con el 'unique_ptr' todavía tiene que eliminar la referencia de los nodos, pero aparte de eso, deberían comportarse de la misma manera. –