C++
Smarta pekare
Sök…
Syntax
-
std::shared_ptr<ClassType> variableName = std::make_shared<ClassType>(arg1, arg2, ...);
-
std::shared_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
-
std::unique_ptr<ClassType> variableName = std::make_unique<ClassType>(arg1, arg2, ...);
// C ++ 14 -
std::unique_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
Anmärkningar
C ++ är inte ett minneshanterat språk. Dynamiskt tilldelat minne (dvs objekt som skapats med new
) "läcker" om det inte uttryckligen omlokaliseras (med delete
). Det är programmerarens ansvar att se till att det dynamiskt tilldelade minnet frigörs innan den sista pekaren kastas till det objektet.
Smarta pekare kan användas för att automatiskt hantera räckvidden för dynamiskt tilldelat minne (dvs. när den sista pekarreferensen går utanför räckvidden raderas den).
Smarta pekare föredras framför "råa" pekare i de flesta fall. De uttrycker ägandesemantiken för dynamiskt tilldelat minne genom att i deras namn kommunicera om ett objekt är avsett att delas eller unikt ägs.
Använd #include <memory>
för att kunna använda smarta pekare.
Dela ägande (std :: shared_ptr)
std::shared_ptr
definierar en delad pekare som kan dela ägande av ett objekt med andra delade pekare. Detta står i kontrast till std::unique_ptr
som representerar exklusivt ägande.
Delningsbeteendet implementeras genom en teknik som kallas referensräkning, där antalet delade pekare som pekar på objektet lagras bredvid det. När denna räkning når noll, antingen genom förstörelse eller omfördelning av den sista instansen std::shared_ptr
, förstörs objektet automatiskt.
// Creation: 'firstShared' is a shared pointer for a new instance of 'Foo'
std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);
För att skapa flera smarta pekare som delar samma objekt måste vi skapa en annan shared_ptr
som alias den första delade pekaren. Här är två sätt att göra det:
std::shared_ptr<Foo> secondShared(firstShared); // 1st way: Copy constructing
std::shared_ptr<Foo> secondShared;
secondShared = firstShared; // 2nd way: Assigning
Ett av ovanstående sätt gör secondShared
en delad pekare som delar ägandet av vår förekomst av Foo
med firstShared
.
Den smarta pekaren fungerar precis som en rå pekare. Det här betyder att du kan använda *
att ta bort dem. Den vanliga ->
operatören fungerar också:
secondShared->test(); // Calls Foo::test()
Slutligen, när den sista alias shared_ptr
går utanför räckvidden, kallas förstöraren av vår Foo
instans.
Varning: Att konstruera en shared_ptr
kan kasta ett bad_alloc
undantag när extra data för semantik för delat ägande behöver tilldelas. Om konstruktören passerar en vanlig pekare antar den att äga föremålet som den pekas på och ringer borttagaren om ett undantag kastas. Detta innebär att shared_ptr<T>(new T(args))
inte läcker ett T
objekt om allokeringen av shared_ptr<T>
misslyckas. Det är dock tillrådligt att använda make_shared<T>(args)
eller allocate_shared<T>(alloc, args)
, vilket gör det möjligt för implementeringen att optimera minnesallokering.
Tilldela Arrays ([]) med hjälp av shared_ptr
Tyvärr finns det inget direkt sätt att tilldela Arrays med make_shared<>
.
Det är möjligt att skapa matriser för shared_ptr<>
med new
och std::default_delete
.
För att tilldela en grupp med 10 heltal kan vi till exempel skriva koden som
shared_ptr<int> sh(new int[10], std::default_delete<int[]>());
std::default_delete
är obligatoriskt att ange std::default_delete
här för att se till att det tilldelade minnet rensas korrekt med delete[]
.
Om vi vet storleken vid sammanställningstiden kan vi göra det på detta sätt:
template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
std::shared_ptr<T> operator()const{
auto r = std::make_shared<std::array<T,N>>();
if (!r) return {};
return {r.data(), r};
}
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }
sedan make_shared_array<int[10]>
en shared_ptr<int>
pekar på 10 ints alla standardkonstruerade.
Med C ++ 17 fick shared_ptr
speciellt stöd för arraytyper. Det är inte längre nödvändigt att specificera array-deleter uttryckligen, och den delade pekaren kan avlägsnas med hjälp av []
array index operator:
std::shared_ptr<int[]> sh(new int[10]);
sh[0] = 42;
Delade pekare kan peka på ett underobjekt för objektet som det äger:
struct Foo { int x; };
std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);
Både p2
och p1
äger föremålet av typen Foo
, men p2
pekar på dess int
medlem x
. Detta innebär att om p1
går ut ur omfattningen eller omtilldelas, kommer det underliggande Foo
objektet fortfarande att leva, vilket säkerställer att p2
inte dinglar.
Viktigt: En shared_ptr
vet bara om sig själv och alla andra shared_ptr
som skapades med aliaskonstruktören. Den vet inte om några andra pekare, inklusive alla andra shared_ptr
er som skapats med en referens till samma Foo
instans:
Foo *foo = new Foo;
std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // don't do this
shared1.reset(); // this will delete foo, since shared1
// was the only shared_ptr that owned it
shared2->test(); // UNDEFINED BEHAVIOR: shared2's foo has been
// deleted already!!
Ägaröverföring av shared_ptr
Som standard shared_ptr
referensräkningen och överför inte ägandet. Det kan emellertid göras att överföra ägandet med std::move
:
shared_ptr<int> up = make_shared<int>();
// Transferring the ownership
shared_ptr<int> up2 = move(up);
// At this point, the reference count of up = 0 and the
// ownership of the pointer is solely with up2 with reference count = 1
Delning med tillfälligt ägande (std :: weak_ptr)
Instanser av std::weak_ptr
kan peka på objekt som ägs av instanser av std::shared_ptr
medan de bara blir tillfälliga ägare själva. Detta innebär att svaga pekare inte ändrar objektets referensräkning och därför inte förhindrar att ett objekt raderas om alla objektets delade pekare omdisponeras eller förstörs.
I följande exempel används instanser av std::weak_ptr
så att förstörelsen av ett trädobjekt inte hämmas:
#include <memory>
#include <vector>
struct TreeNode {
std::weak_ptr<TreeNode> parent;
std::vector< std::shared_ptr<TreeNode> > children;
};
int main() {
// Create a TreeNode to serve as the root/parent.
std::shared_ptr<TreeNode> root(new TreeNode);
// Give the parent 100 child nodes.
for (size_t i = 0; i < 100; ++i) {
std::shared_ptr<TreeNode> child(new TreeNode);
root->children.push_back(child);
child->parent = root;
}
// Reset the root shared pointer, destroying the root object, and
// subsequently its child nodes.
root.reset();
}
När barnnoder läggs till i rodnodens barn, std::weak_ptr
parent
std::weak_ptr
. Medlemmens parent
förklaras som en svag pekare i motsats till en delad pekare så att rotnodens referensantal inte ökas. När rotnoden återställs i slutet av main()
förstörs roten. Eftersom de enda återstående std::shared_ptr
hänvisningar till underordnade noder ingick i roten s insamlings children
, alla underordnade noder därefter förstördes också.
På grund av detaljerad information om kontrollblocken kan det tilldelade minnet för delad_ptr inte släppas förrän shared_ptr
referensräknare för weak_ptr
referensräknare för weak_ptr
båda når noll.
#include <memory>
int main()
{
{
std::weak_ptr<int> wk;
{
// std::make_shared is optimized by allocating only once
// while std::shared_ptr<int>(new int(42)) allocates twice.
// Drawback of std::make_shared is that control block is tied to our integer
std::shared_ptr<int> sh = std::make_shared<int>(42);
wk = sh;
// sh memory should be released at this point...
}
// ... but wk is still alive and needs access to control block
}
// now memory is released (sh and wk)
}
Eftersom std::weak_ptr
inte håller sitt refererade objekt vid liv, är direkt datatillgång genom en std::weak_ptr
inte möjligt. I stället tillhandahåller den en lock()
-medlemfunktion som försöker hämta en std::shared_ptr
till det refererade objektet:
#include <cassert>
#include <memory>
int main()
{
{
std::weak_ptr<int> wk;
std::shared_ptr<int> sp;
{
std::shared_ptr<int> sh = std::make_shared<int>(42);
wk = sh;
// calling lock will create a shared_ptr to the object referenced by wk
sp = wk.lock();
// sh will be destroyed after this point, but sp is still alive
}
// sp still keeps the data alive.
// At this point we could even call lock() again
// to retrieve another shared_ptr to the same data from wk
assert(*sp == 42);
assert(!wk.expired());
// resetting sp will delete the data,
// as it is currently the last shared_ptr with ownership
sp.reset();
// attempting to lock wk now will return an empty shared_ptr,
// as the data has already been deleted
sp = wk.lock();
assert(!sp);
assert(wk.expired());
}
}
Unikt ägande (std :: unique_ptr)
En std::unique_ptr
är en std::unique_ptr
som hanterar livslängden för ett dynamiskt lagrat objekt. Till skillnad från för std::shared_ptr
, std::shared_ptr
det dynamiska objektet endast av en instans av en std::unique_ptr
när som helst,
// Creates a dynamic int with value of 20 owned by a unique pointer
std::unique_ptr<int> ptr = std::make_unique<int>(20);
(Obs: std::unique_ptr
är tillgängligt sedan C ++ 11 och std::make_unique
sedan C ++ 14.)
Endast variabeln ptr
har en pekare till ett dynamiskt tilldelat int
. När en unik pekare som äger ett objekt går utanför räckvidden raderas det ägda objektet, det vill säga dess förstörare kallas om objektet är av klasstyp och minnet för det objektet släpps.
Om du std::unique_ptr
använda std::unique_ptr
och std::make_unique
med array-typer använder du deras array-specialiseringar:
// Creates a unique_ptr to an int with value 59
std::unique_ptr<int> ptr = std::make_unique<int>(59);
// Creates a unique_ptr to an array of 15 ints
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);
Du kan komma åt std::unique_ptr
precis som en rå pekare, eftersom det överbelaster de operatörerna.
Du kan överföra äganderätten till innehållet i en smart pekare till en annan pekare genom att använda std::move
, vilket gör att den ursprungliga smartpekaren pekar på nullptr
.
// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();
// Change value to 1
*ptr = 1;
// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);
int a = *ptr2; // 'a' is 1
int b = *ptr; // undefined behavior! 'ptr' is 'nullptr'
// (because of the move command above)
unique_ptr
till funktioner som parameter:
void foo(std::unique_ptr<int> ptr)
{
// Your code goes here
}
std::unique_ptr<int> ptr = std::make_unique<int>(59);
foo(std::move(ptr))
Återvänder unique_ptr
från funktioner. Detta är det föredragna C ++ 11-sättet att skriva fabriksfunktioner, eftersom det tydligt förmedlar ägarens semantik för returen: den som ringer äger den resulterande unique_ptr
och är ansvarig för det.
std::unique_ptr<int> foo()
{
std::unique_ptr<int> ptr = std::make_unique<int>(59);
return ptr;
}
std::unique_ptr<int> ptr = foo();
Jämför detta med:
int* foo_cpp03();
int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?
// it's not readily apparent what the answer is.
make_unique
tillhandahålls sedan C ++ 14. Det är lätt att lägga till det manuellt i C ++ 11-koden:
template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }
// Use make_unique for arrays
template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }
Till skillnad från den dumma smarta pekaren ( std::auto_ptr
) kan unique_ptr
också instanseras med vektortilldelning ( inte std::vector
). Tidigare exempel var för skalära tilldelningar. För att till exempel ha en dynamiskt tilldelad heltal-array för 10 element, skulle du ange int[]
som malltyp (och inte bara int
):
std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);
Vilket kan förenklas med:
auto arr_ptr = std::make_unique<int[]>(10);
Nu använder du arr_ptr
som om det är en matris:
arr_ptr[2] = 10; // Modify third element
Du behöver inte oroa dig för allokering. Denna mallspecifika version kallar konstruktörer och destruktorer på lämpligt sätt. Att använda vektordragen version av unique_ptr
eller en vector
sig - är ett personligt val.
I versioner före C ++ 11 var std::auto_ptr
tillgängligt. Till skillnad från unique_ptr
är det tillåtet att kopiera auto_ptr
s, på vilket källan ptr
kommer att förlora ägandet av den medföljande pekaren och målet får den.
Använda anpassade rader för att skapa en omslag till ett C-gränssnitt
Många C-gränssnitt som SDL2 har sina egna raderingsfunktioner. Detta innebär att du inte kan använda smarta pekare direkt:
std::unique_ptr<SDL_Surface> a; // won't work, UNSAFE!
Istället måste du definiera din egen raderare. Exemplen här använder SDL_Surface
strukturen som bör frigöras med SDL_FreeSurface()
, men de bör vara anpassningsbara till många andra C-gränssnitt.
Raderaren måste kunna kallas med ett pekarargument och kan därför vara en enkel funktionspekare:
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);
Alla andra utskrivbara objekt fungerar också, till exempel en klass med en operator()
:
struct SurfaceDeleter {
void operator()(SDL_Surface* surf) {
SDL_FreeSurface(surf);
}
};
std::unique_ptr<SDL_Surface, SurfaceDeleter> a(pointer, SurfaceDeleter{}); // safe
std::unique_ptr<SDL_Surface, SurfaceDeleter> b(pointer); // equivalent to the above
// as the deleter is value-initialized
Detta ger dig inte bara säker, noll överhead (om du använder unique_ptr
) automatisk minneshantering, du får också undantagssäkerhet.
Observera att borttagaren är en del av typen för unique_ptr
, och implementeringen kan använda den tomma basoptimeringen för att undvika ändringar i storlek för tomma anpassade borttagare. Så medan std::unique_ptr<SDL_Surface, SurfaceDeleter>
och std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)>
löser samma problem på liknande sätt, är den tidigare typen fortfarande bara storleken på en pekare medan den senare typen måste ha två pekare: både SDL_Surface*
och funktionspekaren! När du har anpassade rader för fri funktion är det att föredra att slå in funktionen i en tom typ.
I fall där referensräkning är viktig kan man använda en shared_ptr
istället för en unique_ptr
. Den shared_ptr
lagrar alltid en deleter, detta raderar typen av radern, vilket kan vara användbart i API: er. Nackdelarna med att använda shared_ptr
jämfört med unique_ptr
inkluderar en högre minneskostnad för lagring av borttagaren och en prestandakostnad för att bibehålla referensräkningen.
// deleter required at construction time and is part of the type
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);
// deleter is only required at construction time, not part of the type
std::shared_ptr<SDL_Surface> b(pointer, SDL_FreeSurface);
Med template auto
kan vi göra det ännu enklare att packa in våra anpassade raders:
template <auto DeleteFn>
struct FunctionDeleter {
template <class T>
void operator()(T* ptr) {
DeleteFn(ptr);
}
};
template <class T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;
Med vilket ovanstående exempel helt enkelt är:
unique_ptr_deleter<SDL_Surface, SDL_FreeSurface> c(pointer);
Här är syftet med auto
att hantera alla gratisfunktioner, oavsett om de returnerar void
(t.ex. SDL_FreeSurface
) eller inte (t.ex. fclose
).
Unikt ägande utan flyttande semantik (auto_ptr)
OBS: std::auto_ptr
har avskrivits i C ++ 11 och kommer att tas bort i C ++ 17. Du bör bara använda detta om du tvingas använda C ++ 03 eller tidigare och är villig att vara försiktig. Det rekommenderas att gå till unikt_ptr i kombination med std::move
att ersätta std::auto_ptr
beteende.
Innan vi hade std::unique_ptr
, innan vi hade flyttat semantik, hade vi std::auto_ptr
. std::auto_ptr
ger unikt ägande men överför äganderätt vid kopia.
Som med alla smarta pekare std::auto_ptr
automatiskt upp resurser (se RAII ):
{
std::auto_ptr<int> p(new int(42));
std::cout << *p;
} // p is deleted here, no memory leaked
men tillåter bara en ägare:
std::auto_ptr<X> px = ...;
std::auto_ptr<X> py = px;
// px is now empty
Detta gör det möjligt att använda std :: auto_ptr för att hålla ägandet uttryckligt och unikt vid risken att förlora ägandet oavsiktligt:
void f(std::auto_ptr<X> ) {
// assumes ownership of X
// deletes it at end of scope
};
std::auto_ptr<X> px = ...;
f(px); // f acquires ownership of underlying X
// px is now empty
px->foo(); // NPE!
// px.~auto_ptr() does NOT delete
Överföringen av ägandet skedde i "kopia" -konstruktören. auto_ptr
kopieringskonstruktör och kopieringsuppdragsoperatör tar sina operander genom icke- const
referens så att de kunde modifieras. Ett exempel på implementering kan vara:
template <typename T>
class auto_ptr {
T* ptr;
public:
auto_ptr(auto_ptr& rhs)
: ptr(rhs.release())
{ }
auto_ptr& operator=(auto_ptr& rhs) {
reset(rhs.release());
return *this;
}
T* release() {
T* tmp = ptr;
ptr = nullptr;
return tmp;
}
void reset(T* tmp = nullptr) {
if (ptr != tmp) {
delete ptr;
ptr = tmp;
}
}
/* other functions ... */
};
Detta bryter kopieringssemantik, som kräver att kopiering av ett objekt ger dig två likvärdiga versioner av det. För alla kopierbara typer, T
, skulle jag kunna skriva:
T a = ...;
T b(a);
assert(b == a);
Men för auto_ptr
är detta inte fallet. Som ett resultat är det inte säkert att lägga auto_ptr
i containrar.
Få en shared_ptr som hänvisar till detta
enable_shared_from_this
gör att du kan få en giltig shared_ptr
instans till this
.
Genom att härleda din klass från enable_shared_from_this
ärver du en metod shared_from_this
som returnerar en shared_ptr
instans till this
.
Observera att objektet måste skapas som en shared_ptr
början:
#include <memory>
class A: public enable_shared_from_this<A> {
};
A* ap1 =new A();
shared_ptr<A> ap2(ap1); // First prepare a shared pointer to the object and hold it!
// Then get a shared pointer to the object from the object itself
shared_ptr<A> ap3 = ap1->shared_from_this();
int c3 =ap3.use_count(); // =2: pointing to the same object
Obs (2) du kan inte ringa enable_shared_from_this
inom konstruktören.
#include <memory> // enable_shared_from_this
class Widget : public std::enable_shared_from_this< Widget >
{
public:
void DoSomething()
{
std::shared_ptr< Widget > self = shared_from_this();
someEvent -> Register( self );
}
private:
...
};
int main()
{
...
auto w = std::make_shared< Widget >();
w -> DoSomething();
...
}
Om du använder shared_from_this()
på ett objekt som inte ägs av ett shared_ptr
, till exempel ett lokalt automatiskt objekt eller ett globalt objekt, är beteendet odefinierat. Eftersom C ++ 17 kastar det std::bad_alloc
istället.
Att använda shared_from_this()
från en konstruktör motsvarar det att använda det på ett objekt som inte ägs av en shared_ptr
, eftersom objekten besattes av den shared_ptr
efter att konstruktören återvänder.
Casting std :: shared_ptr-pekare
Det är inte möjligt att direkt använda static_cast
, const_cast
, dynamic_cast
och reinterpret_cast
på std::shared_ptr
att hämta en pekare som delar ägandet med pekaren som skickas som argument. Istället std::static_pointer_cast
funktionerna std::static_pointer_cast
, std::const_pointer_cast
, std::dynamic_pointer_cast
och std::reinterpret_pointer_cast
användas:
struct Base { virtual ~Base() noexcept {}; };
struct Derived: Base {};
auto derivedPtr(std::make_shared<Derived>());
auto basePtr(std::static_pointer_cast<Base>(derivedPtr));
auto constBasePtr(std::const_pointer_cast<Base const>(basePtr));
auto constDerivedPtr(std::dynamic_pointer_cast<Derived const>(constBasePtr));
Observera att std::reinterpret_pointer_cast
inte är tillgängligt i C ++ 11 och C ++ 14, eftersom det endast föreslogs av N3920 och antogs i Library Fundamentals TS i februari 2014 . Det kan emellertid implementeras enligt följande:
template <typename To, typename From>
inline std::shared_ptr<To> reinterpret_pointer_cast(
std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }
Att skriva en smart pekare: value_ptr
En value_ptr
är en smart pekare som uppträder som ett värde. När den kopieras kopierar den innehållet. När det skapas skapar det innehållet.
// Like std::default_delete:
template<class T>
struct default_copier {
// a copier must handle a null T const* in and return null:
T* operator()(T const* tin)const {
if (!tin) return nullptr;
return new T(*tin);
}
void operator()(void* dest, T const* tin)const {
if (!tin) return;
return new(dest) T(*tin);
}
};
// tag class to handle empty case:
struct empty_ptr_t {};
constexpr empty_ptr_t empty_ptr{};
// the value pointer type itself:
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {
using copier_type=Copier;
// also typedefs from unique_ptr
using Base::Base;
value_ptr( T const& t ):
Base( std::make_unique<T>(t) ),
Copier()
{}
value_ptr( T && t ):
Base( std::make_unique<T>(std::move(t)) ),
Copier()
{}
// almost-never-empty:
value_ptr():
Base( std::make_unique<T>() ),
Copier()
{}
value_ptr( empty_ptr_t ) {}
value_ptr( Base b, Copier c={} ):
Base(std::move(b)),
Copier(std::move(c))
{}
Copier const& get_copier() const {
return *this;
}
value_ptr clone() const {
return {
Base(
get_copier()(this->get()),
this->get_deleter()
),
get_copier()
};
}
value_ptr(value_ptr&&)=default;
value_ptr& operator=(value_ptr&&)=default;
value_ptr(value_ptr const& o):value_ptr(o.clone()) {}
value_ptr& operator=(value_ptr const&o) {
if (o && *this) {
// if we are both non-null, assign contents:
**this = *o;
} else {
// otherwise, assign a clone (which could itself be null):
*this = o.clone();
}
return *this;
}
value_ptr& operator=( T const& t ) {
if (*this) {
**this = t;
} else {
*this = value_ptr(t);
}
return *this;
}
value_ptr& operator=( T && t ) {
if (*this) {
**this = std::move(t);
} else {
*this = value_ptr(std::move(t));
}
return *this;
}
T& get() { return **this; }
T const& get() const { return **this; }
T* get_pointer() {
if (!*this) return nullptr;
return std::addressof(get());
}
T const* get_pointer() const {
if (!*this) return nullptr;
return std::addressof(get());
}
// operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
return {std::make_unique<T>(std::forward<Args>(args)...)};
}
Detta specifika värde_ptr är bara tomt om du konstruerar det med empty_ptr_t
eller om du flyttar från det. Det avslöjar det faktum att det är en unique_ptr
, så explicit operator bool() const
fungerar på den. .get()
har ändrats för att returnera en referens (eftersom den nästan aldrig är tom), och .get_pointer()
returnerar en pekare istället.
Den här smarta pekaren kan vara användbar för pImpl
fall, där vi vill ha värde-semantik men vi vill inte heller exponera innehållet i pImpl
utanför implementeringsfilen.
Med en icke-standard Copier
, kan det till och med hantera virtuella basklasser som vet hur man producerar förekomster av deras härledda och förvandla dem till värdetyper.