unique_ptr smart shared_ptr make_unique c++ c++14 unique-ptr

c++ - smart - Ventajas de usar std:: make_unique sobre el nuevo operador



shared_ptr (2)

Ventajas

  • make_unique enseña a los usuarios "nunca digas new / delete y new[] / delete[] " sin renuncias.

  • make_unique comparte dos ventajas con make_shared (excluyendo la tercera ventaja, mayor eficiencia). Primero, unique_ptr<LongTypeName> up(new LongTypeName(args)) debe mencionar LongTypeName dos veces, mientras que auto up = make_unique<LongTypeName>(args) menciona una vez.

  • make_unique evita la pérdida de orden de evaluación no especificada desencadenada por expresiones como foo(unique_ptr<X>(new X) , unique_ptr<Y>(new Y)) . (Seguir el consejo "nunca digas new " es más simple que "nunca digas new , a menos que lo des inmediatamente a un unique_ptr ").

  • make_unique se implementa cuidadosamente para garantizar la seguridad de las excepciones y se recomienda antes de llamar directamente a los constructores unique_ptr .

Cuando no use make_unique

  • No use make_unique si necesita un eliminador personalizado o si está adoptando un puntero sin formato desde otro lugar.

Fuentes

  1. Propuesta de std::make_unique .
  2. Solución GotW # 89 de Herb Sutter: punteros inteligentes

Esta pregunta ya tiene una respuesta aquí:

¿Cuáles son las ventajas de usar std::make_unique sobre el new operador para inicializar std::unique_ptr ?

En otras palabras, ¿por qué es

std::unique_ptr<SomeObject> a = std::make_unique(SomeObject(...))

mejor que hacerlo

std::unique_ptr<SomeObject> a = new SomeObject(...)

Intenté buscar mucho en línea y sé que es una buena regla general evitar al operador new en C ++ moderno, pero no estoy seguro de cuáles son las ventajas en este escenario exacto. ¿Previene cualquier tipo de pérdida de memoria que pueda ocurrir? ¿Es más rápido hacer un std::make_unique que usar new ?


La diferencia es que std::make_unique devuelve un objeto de tipo std::unique_ptr y new devuelve un puntero al objeto creado. Para fallas de asignación de memoria, ambos arrojarán. Espera, no es tan simple. Leer más

Considere tal función a continuación:

void func(ClassA* a, ClassB* b){ ...... }

Cuando realiza una llamada como func(new A(), new B()) ; El compilador puede elegir evaluar los argumentos de la función de izquierda a derecha, o en el orden que lo desee. Asumamos una evaluación de izquierda a derecha: ¿Qué sucede cuando la primera expresión new tiene éxito pero la segunda expresión new arroja?

El verdadero peligro aquí es cuando atrapas esa excepción; Sí, puede haber detectado la excepción lanzada por el new B() y reanudar la ejecución normal, pero el new A() ya tuvo éxito y su memoria se filtrará en silencio. Nadie para limpiarlo ... * sollozos ...

Pero con make_unique , no puede haber una fuga porque sucederá el desenrollado de la pila (y se ejecutará el destructor del objeto creado anteriormente). Por lo tanto, tener preferencia por make_unique lo make_unique a la seguridad de excepción . En este caso, std::make_unique proporciona una " Seguridad de excepción básica " de que la memoria asignada y el objeto creado por new nunca quedarán huérfanos std::make_unique . Incluso hasta el final de los tiempos ... :-)

Deberías leer Herb Sutter GoTW102