Zowel unique_ptr
als shared_ptr
accepteren een aangepast verwijderprogramma om het object dat ze bezitten aan te roepen. Maar in het geval van unique_ptr
wordt de verwijderaar doorgegeven als een sjabloonparameter van de klasse, terwijl het type aangepaste verwijderaar van shared_ptr
is te specificeren als een sjabloonparameter van de constructor.
template <class T, class D = default_delete<T>>
class unique_ptr
{
unique_ptr(T*, D&); //simplified
...
};
en
template<class T>
class shared_ptr
{
template<typename D>
shared_ptr(T*, D); //simplified
...
};
Ik begrijp niet waarom zo’n verschil. Wat vereist dat?
Antwoord 1, autoriteit 100%
Als je de deleter opgeeft als sjabloonargument (zoals in unique_ptr
), maakt het deel uit van het type en hoef je niets extra’s op te slaan in de objecten van dit type.
Als deleter wordt doorgegeven als argument van de constructor (zoals in shared_ptr
), moet je het in het object opslaan. Dit zijn de kosten van extra flexibiliteit, aangezien u verschillende verwijderaars kunt gebruiken voor de objecten van hetzelfde type.
Ik denk dat dit de reden is: unique_ptr
wordt verondersteld een zeer lichtgewicht object te zijn zonder overhead. Het opslaan van verwijderaars bij elke unique_ptr
zou hun grootte kunnen verdubbelen. Daarom zouden mensen in plaats daarvan goede oude onbewerkte wijzers gebruiken, wat verkeerd zou zijn.
Aan de andere kant is shared_ptr
niet zo licht, omdat het referentieaantal moet opslaan, dus het opslaan van een aangepaste deleter lijkt ook een goede afweging.
Antwoord 2, autoriteit 5%
Gedeelde pointers van verschillende typenkunnen het eigendom van het hetzelfde objectdelen. Zie overbelasting (8)van std::shared_ptr::shared_ptr
. Unieke pointers hebben zo’n mechanisme niet nodig, omdat ze niet delen.
template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;
Als je de deleter niet had gewist, zou je zo’n shared_ptr<T, Y_Deleter>
niet kunnen gebruiken als een shared_ptr<T>
, wat het eigenlijk nutteloos zou maken.
Waarom zou je zo’n overbelasting willen?
Overweeg
struct Member {};
struct Container { Member member };
Als je de Container
in leven wilt houden, terwijl je het Member
gebruikt, dan kan dat
std::shared_ptr<Container> pContainer = /* something */
std::shared_ptr<Member> pMember(pContainer, &pContainer->member);
en alleen pMember
vast te houden (misschien zet het in een std::vector<std::shared_ptr<Member>>
)
Of anders overbelasting (9) gebruiken
template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;
// Only exists if Y* is implicitly convertible to T*
U kunt polymorf delen hebben
struct Base {};
struct Derived : Base {};
void operate_on_base(std::shared_ptr<Base>);
std::shared_ptr<Derived> pDerived = /* something*/
operate_on_base(pDerived);