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

C ++ 11 C ++ 17

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.

C ++ 17

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)

C ++ 11

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.
C ++ 14

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]()); }
C ++ 11

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); 
C ++ 17

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)

C ++ 11

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_caststd::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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow