c++ - shared_ptr - ¿Por qué std:: make_unique en lugar de std:: unique_ptr:: make?
c++14 smart-pointers (3)
No hay una razón concreta aparte de la convención sola: una función de clase estática puede hacer todo lo que puede hacer una función global (funcionalidad inteligente).
C ++ prefiere las funciones globales (para funciones de utilidad) que se encuentran dentro de un espacio de nombres definido.
Otros lenguajes de programación (como Java) prefieren las funciones públicas estáticas, ya que las funciones globales no son compatibles.
esto no es nuevo para make_***
, existen otros ejemplos:
std::this_thread::XXXX
lugar de std::thread::XXXX_current
aunque podría tener sentido poner una función que se relaciona con el subproceso de ejecución actual como funciones estáticas dentro de la clase de thread
, se hacen globales dentro del espacio de nombres de este this_thread
.
también, podríamos tener algo como std::container::sort
que std::container
es una clase auxiliar para contenedores, pero en su lugar tenemos std::sort
.
¿Por qué C ++ adoptó funciones gratuitas para:
std::make_unique(...);
std::make_shared(...);
En lugar de usar funciones miembro estáticas:
std::unique_ptr::make(...); // static
std::shared_ptr::make(...); // static
?
TL; DR: las funciones miembro estáticas siempre tienen acceso a datos privados, pero las funciones gratuitas solo tienen acceso a datos privados cuando se marcan explícitamente como friend
. La elección de implementar estas funciones como funciones gratuitas (con un pequeño número implementado como funciones de amistad) no es un artefacto histórico aleatorio, sino una decisión deliberada para mejorar la encapsulación mientras se tiene un esquema de nombres coherente para todos los std::make_x
funciones
Hay muchas funciones estándar de fábrica en C ++:
std::make_pair
std::make_tuple
std::make_unique
std::make_shared //efficiency
std::make_exception_ptr //efficiency
std::make_move_iterator
std::make_reverse_iterator
std::make_error_code
std::make_error_condition
//And several more are proposed for C++17
Por todo lo anterior, la función make_x
se puede implementar correctamente utilizando solo la interfaz pública de x
. En el caso de make_shared
y make_exception_ptr
, la implementación más eficiente requeriría acceso a los datos internos de std::shared_ptr
o std::exception_ptr
. Todos los demás se pueden implementar utilizando solo la interfaz pública con cero penalización de rendimiento.
La implementación de estas funciones como funciones libres que no son de amigos reduce la cantidad de código que tiene acceso a las partes internas privadas del objeto ( una propiedad deseable , ya que cuando menos código tiene acceso a datos privados, hay menos lugares que deben ser auditados). operaciones que violan las invariantes del objeto, y menos lugares que potencialmente necesitan ser cambiados si cambian las partes internas del objeto).
Si make_shared
fuera la única función de fábrica similar, podría tener sentido que sea una función miembro, pero como la mayoría de estas funciones no tienen que ser funciones de friend
para funcionar de manera eficiente, make_shared
también se implementa como una función gratuita, por ejemplo. el bien de la consistencia.
Este es el diseño correcto, ya que si las funciones de make_shared
miembros estáticas se usaran de forma sistemática, en todos los casos, aparte de make_shared
y make_exception_ptr
, la función de miembro tendría inevitablemente un acceso excesivo a los datos privados del objeto x
. Con el diseño estandarizado, el pequeño número de funciones make_x
que necesitan acceso a datos privados se puede marcar como friend
, y el resto respeta la encapsulación correctamente de forma predeterminada. Si se utilizara un make_x
no miembro en algunos casos y un miembro estático en otros, la biblioteca estándar se volvería inconsistente y más difícil de aprender.
Consistencia
No creo que haya ninguna razón convincente para tener la sintaxis ::make
lugar de la actual. Supongo que make_unique
y make_shared
se prefirieron a una función static ::make
para mantener la coherencia con las funciones existentes std::make_pair
y std::make_heap
, que existían antes de C ++ 11.
Tenga en cuenta que std::make_pair
tiene una gran ventaja: deduce automáticamente los tipos del par resultante de la llamada a la función:
auto p0 = std::make_pair(1, 1.f); // pair<int, float>
Si tuviéramos std::pair::make
, tendríamos que escribir:
auto p1 = std::pair<int, float>::make(1, 1.f);
que derrota el propósito de make_pair
.
Por lo tanto, asumo que
make_unique
ymake_shared
fueron elegidos porque los desarrolladores ya estaban acostumbrados amake_pair
y funciones similares.Se eligió
make_pair
lugar depair::make
para los beneficios mencionados anteriormente.