2015-02-19 23 views
5

Sto tentando di utilizzare unique_ptr con un deleter personalizzato per il tipo SDL_Surface. Questo è solo un esempio con il tipo int, ma spero che tu ne abbia l'idea.Confuso utilizzando unique_ptr e un deleter personalizzato

#include <iostream> 
#include <functional> 
#include <memory> 

typedef int SDL_Surface; 


SDL_Surface * CreateSurface() 
{ 
    SDL_Surface * p = new SDL_Surface; 
    return p; 
} 

void FreeSurface(SDL_Surface *p) 
{ 
    delete p; 
} 

int main() { 
    std::unique_ptr<SDL_Surface, std::function< void (SDL_Surface *) > > uptr_1; 

    //how to assign a value to uptr_1 and the deleter? 

    return 0; 
} 

Is uptr_1 correttamente dichiarato e inizializzato a nullptr? In tal caso, come posso assegnare il puntatore e la funzione di cancellazione?

E come posso incapsulare questo: std::unique_ptr< SDL_Surface, std::function< void (SDL_Surface *) > > con la delezione di non sempre scrivere che la linea su ogni SDL_Surface voglio, un'altra typedef?

Sto appena iniziando a imparare le funzionalità di C++ 11 e questo è difficile per me.

+0

'std :: unique_ptr > uptr_1 (CreateSurface (), & :: FreeSurface); ' –

+5

' std :: function' è una scelta sbagliata per un deleter nel caso generale, perché i suoi costruttori possono lanciare, ma il costruttore di deleter di 'unique_ptr' non deve lanciare. Tuttavia, se lo stai usando solo con un semplice puntatore a funzione, è sicuro. –

+0

Correlati - https://stackoverflow.com/q/24251747/241631 – Praetorian

risposta

10

È possibile inizializzare l'unique_ptr con un puntatore e deleter, o utilizzare = normalmente se ri-assegnazione tardi:

std::unique_ptr<SDL_Surface, std::function<void (SDL_Surface *)>> uptr_1(CreateSurface(), &FreeSurface); 

uptr_1 = std::unique_ptr<SDL_Surface, std::function<void (SDL_Surface *)>>(CreateSurface(), &FreeSurface); 

Fare riferimento alla suitable docs per i dettagli.

per abbreviare il tipo lungo, è possibile infatti utilizzare un tipo alias (o typedefusing):

typedef std::unique_ptr<SDL_Surface, void (*)(SDL_Surface*)> Surface_ptr; 

//or 

using Surface_ptr = std::unique_ptr<SDL_Surface, void (*)(SDL_Surface*)>; 

Avviso realtà ho usato void (*)(SDL_Surface*) per il tipo di deleter. Se sai che passerai sempre una funzione reale (o lambda senza stato) in, non c'è motivo di trascinare in std::function, che ha un sovraccarico dovuto alla cancellazione dei tipi.

Inoltre, è possibile ridurre ancora di più con la creazione di un funtore default-costruibile per il deleter:

struct FreeSurface_Functor 
{ 
    void operator() (SDL_Surface *s) const 
    { 
    FreeSurface(s); 
    } 
}; 

questo modo, si può fare il tipo di puntatore std::unique_ptr<SDL_Surface, FreeSurface_Functor> (possibilmente alias) e si don' t fornire il deleter; sarà default-costruito:

std::unique_ptr<SDL_Surface, FreeSurface_Functor> uptr_1(CreateSurface()); 
+0

devo controllare il delet personalizzato se il puntatore è nullptr? Oppure std :: unique_ptr non chiamerà il delet predefinito se il puntatore è nullptr? Grazie per la risposta. – FrameBuffer

+1

@FrameBuffer Non è necessario controllare. 'unique_ptr' è garantito per fare il controllo per te, e solo chiamare il deleter se non-null. Puoi trovare cose del genere in [i documenti che ho collegato] (http://en.cppreference.com/w/cpp/memory/unique_ptr/~unique_ptr). – Angew

+5

Si potrebbe sottolineare che la tecnica di definizione di un tipo di classe deleter stateless è preferibile poiché le implementazioni di qualità 'unique_ptr' ottimizzeranno la loro memorizzazione con l'Empty Base Optimization. – Casey

1

È uptr_1 correttamente dichiarato e inizializzato a nullptr

Sì, un default costruito unique_ptr farà riferimento a null.

in tal caso, come è possibile assegnare il puntatore e la funzione di cancellazione?

Si dovrebbe costruire la unique_ptr con argomenti

std::unique_ptr<SDL_Surface, std::function< void (SDL_Surface *) > > uptr_1{CreateSurface(), FreeSurface}; 

In alternativa, dopo la costruzione di default è possibile utilizzare l'assegnazione movimento con una temporanea

uptr_1 = std::unique_ptr<SDL_Surface, std::function< void (SDL_Surface *) > >{CreateSurface(), FreeSurface}; 

Come avete suggerito voi stessi, un digitare alias può aiutare

using SDL_Uptr = std::unique_ptr<SDL_Surface, std::function< void (SDL_Surface *)>>; 
SDL_Uptr uptr_1; 
uptr_1 = SDL_Uptr{CreateSurface(), FreeSurface}; 

Una funzione intermedia potrebbe aiutare a semplificare questo se diventa ripetitivo (cosa che probabilmente succederà se ne fate un sacco).

std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)> 
make_sdl_ptr() { 
    return std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)>{CreateSurface(), FreeSurface}; 
} 

È quindi possibile chiamare questo con auto uptr = make_sdl_ptr();

risposta di Angew con una delezione DefaultConstructible chiamando la funzione è anche una bella soluzione.

0

vorrei andare con decltype:

std::unique_ptr<SDL_Surface, decltype(&FreeSurface)> uptr_1(
      CreateSurface(), 
      FreeSurface 
); 
Problemi correlati