c++ - smart - ¿Por qué unique_ptr toma dos parámetros de plantilla cuando shared_ptr solo toma uno?
unique_ptr c++ (2)
Ambos unique_ptr
y shared_ptr
aceptan un destructor personalizado para llamar al objeto que poseen. Pero en el caso de unique_ptr
, el destructor se pasa como un parámetro de plantilla de la clase , mientras que el tipo de destructor personalizado de shared_ptr
se debe especificar como un parámetro de plantilla del constructor .
template <class T, class D = default_delete<T>>
class unique_ptr
{
unique_ptr(T*, D&); //simplified
...
};
y
template<class T>
class shared_ptr
{
template<typename D>
shared_ptr(T*, D); //simplified
...
};
No puedo ver por qué tanta diferencia. ¿Qué requiere eso?
Los punteros compartidos de diferentes tipos pueden compartir la propiedad del mismo objeto . Ver sobrecarga (8) de std::shared_ptr::shared_ptr
. Los punteros únicos no necesitan este mecanismo, ya que no comparten .
template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;
Si no escribió el borrado, no podría usar dicho shared_ptr<T, Y_Deleter>
como shared_ptr<T>
, lo que básicamente lo haría inútil.
¿Por qué querrías tal sobrecarga?
Considerar
struct Member {};
struct Container { Member member };
Si desea mantener vivo el Container
, mientras usa el Member
, puede hacer
std::shared_ptr<Container> pContainer = /* something */
std::shared_ptr<Member> pMember(pContainer, &pContainer->member);
y solo tiene que aferrarse a pMember
(tal vez ponerlo en std::vector<std::shared_ptr<Member>>
)
O alternativamente, usando sobrecarga (9)
template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;
// Only exists if Y* is implicitly convertible to T*
Puedes tener intercambio polimórfico
struct Base {};
struct Derived : Base {};
void operate_on_base(std::shared_ptr<Base>);
std::shared_ptr<Derived> pDerived = /* something*/
operate_on_base(pDerived);
Si proporciona el eliminador como argumento de plantilla (como en unique_ptr
), es parte del tipo y no necesita almacenar nada adicional en los objetos de este tipo. Si el delegado se pasa como argumento del constructor (como en shared_ptr
), debe almacenarlo en el objeto. Este es el costo de la flexibilidad adicional, ya que puede usar diferentes eliminadores para los objetos del mismo tipo.
Supongo que esta es la razón: se supone que unique_ptr
es un objeto muy liviano con cero gastos generales. El almacenamiento de los eliminadores con cada unique_ptr
podría duplicar su tamaño. Debido a eso, la gente usaría buenos punteros crudos en su lugar, lo que sería incorrecto.
Por otro lado, shared_ptr
no es tan liviano, ya que necesita almacenar el recuento de referencias, por lo que almacenar un eliminador personalizado también parece un buen intercambio.