2012-04-25 18 views
26

Recentemente ho iniziato il porting un sacco di mia C++ esistente codice di applicazione a oltre al C++ 11 e ora che sto convertendo al nuovo puntatori intelligenti std :: unique_ptr e std: : shared_ptr, ho una domanda specifica sui deleteri personalizzati. Voglio aggiungere un logger lambda per vedere dove vengono chiamate le mie eliminazioni, ma non riesco a compilare la versione di specializzazione dell'array. Il consiglio sarebbe molto apprezzato.unique_ptr <T> lambda deleter personalizzato per array di specializzazione

ho cercato invano un esempio di deleter personalizzato per array di specializzazione unique_ptr per VC++ 10 o GCC 4.5.2+. Vorrei stampare un messaggio di log quando i delet sono chiamati in una lambda, principalmente per assicurarsi che tutti i puntatori che penso vadano fuori ambito lo facciano. Questo è possibile per la versione dell'array della specializzazione? Posso farlo funzionare con la versione non array, e posso anche farlo funzionare con una specializzazione di matrice se passo una struttura esterna "MyArrayDeleter" come secondo argomento. Un'altra cosa, sarebbe possibile rimuovere la brutta std :: function come pensavo di poter lasciare che la firma lambda lo indicasse.

struct MySimpleDeleter { 
    void operator()(int* ptr) const { 
     printf("Deleting int pointer!\n"); 
     delete ptr; 
    } 
}; 
struct MyArrayDeleter { 
    void operator()(int* ptr) const { 
     printf("Deleting Array[]!\n"); 
     delete [] ptr; 
    } 
}; 
{ 
    // example 1 - calls MySimpleDeleter where delete simple pointer is called 
    std::unique_ptr<int, MySimpleDeleter> ptr1(new int(5)); 

    // example 2 - correctly calls MyArrayDeleter where delete[] is called 
    std::unique_ptr<int[], MyArrayDeleter> ptr2(new int[5]); 

    // example 3 - this works (but default_delete<int[]> would have been passed 
    // even if I did not specialize it as it is the default second arg 
    // I only show it here to highlight the problem I am trying to solve 
    std::unique_ptr<int[], std::default_delete<int[]>> ptr2(new int[100]); 

    // example 3 - this lambda is called correctly - I want to do this for arrays 
    std::unique_ptr<int, std::function<void (int *)>> ptr3(
     new int(3), [&](int *ptr){ 
      delete ptr; std::cout << "delete int* called" << std::endl; 
     }); 

    // example 4 - I cannot get the following like to compile 
    // PLEASE HELP HERE - I cannot get this to compile 
    std::unique_ptr<int[], std::function<void (int *)>> ptr4(
     new int[4], [&](int *ptr){ 
      delete []ptr; std::cout << "delete [] called" << std::endl; 
     }); 
} 

The compiler error is as follows: 

The error from the compiler (which complains about the new int[4] for ptr4 below is: 
'std::unique_ptr<_Ty,_Dx>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty,_Dx>' 
1>   with 
1>   [ 
1>    _Ty=int [], 
1>    _Dx=std::tr1::function<void (int *)> 
1>   ] 
1>   c:\program files (x86)\microsoft visual studio 10.0\vc\include\memory(2513) : see declaration of 'std::unique_ptr<_Ty,_Dx>::unique_ptr' 
1>   with 
1>   [ 
1>    _Ty=int [], 
1>    _Dx=std::tr1::function<void (int *)> 
1>   ] 
+0

esempio 3 mi ha salvato .. grazie –

risposta

32

Che dire:

auto deleter=[&](int* ptr){...}; 
std::unique_ptr<int[], decltype(deleter)> ptr4(new int[4], deleter); 
+2

Il tipo di argomento del deleter è errato - - che dovrebbe essere 'int * ptr' piuttosto che' int (* ptr) [] '. – ildjarn

+0

@ildjarn: perché' int (* ptr) [] 'sbagliato? Non dovrei aspettarmi' std :: unique_ptr ' passare 'deleter' un puntatore a un array di' int's invece di un puntatore a un singolo int? O è scritto 'int * (ptr [])'? – Managu

+4

In C++, un array dinamico di 'int's è rappresentato come un 'int *'; cioè, il tipo di 'new int [4]' è 'int *'. Di conseguenza, il deleter si aspetta un 'int *' - perché sarebbe utile/necessario un livello extra di riferimento indiretto ? Questo è facilmente dimostrato cercando in realtà di _compilare il tuo codice ..: [non funziona] (http://ideone.com/fujAk), [funziona] (http://ideone.com/JeXYD). nessun contesto è 'int (* ptr) []' sintassi valida; per lo meno dovrebbe essere 'int (* ptr) [N]' per alcuni noti 'N', per fungere da puntatore ad un array di dimensioni _staticamente_. – ildjarn

4

Prima di prima, io uso VC2010 con SP1, Mingw g ++ 4.7.1

Per nuovo allineamento, unique_ptr già sostenerla in modo pulito:

struct X 
{ 
    X() { puts("ctor"); } 
    ~X() { puts("dtor"); } 
}; 

unique_ptr<X[]> xp(new X[3]); 

Il risultato è:

ctor 
ctor 
ctor 
dtor 
dtor 
dtor 

Per deleter personalizzato, purtroppo, è incoerente tra VC2010 e g ++:

VC2010:

unique_ptr<FILE, function<void (FILE*)> > fp(fopen("tmp.txt", "w"), [](FILE *fp){ 
    puts("close file now"); 
    fclose(fp); 
    }); 

g ++:

unique_ptr<FILE, void (*)(FILE*) > fp(fopen("tmp.txt", "w"), [](FILE *fp){ 
    puts("close file now"); 
    fclose(fp); 
    }); 

Il metodo con Managu è molto bene, perché in linea lambda è bello ma fa male a leggere IMHO. Inoltre, enfatizza quella risorsa di rilascio prima dell'acquisizione (RAII).

Qui vi suggerisco un modo per separare declartive acquisizione delle risorse e rilasciare (Guardia Scope, funziona sia per VC2010 e g ++ 4.7.1):

template<typename T> 
struct ScopeGuard 
{ 
    T deleter_; 
    ScopeGuard(T deleter) : deleter_(deleter) {} 
    ~ScopeGuard() { deleter_() ; } 
}; 
#define UNI_NAME(name, line) name ## line 
#define ON_OUT_OF_SCOPE_2(lambda_body, line) auto UNI_NAME(deleter_lambda_, line) = [&]() { lambda_body; } ; \ 
     ScopeGuard<decltype(UNI_NAME(deleter_lambda_, line))> \ 
     UNI_NAME(scope_guard_, line) (UNI_NAME(deleter_lambda_, line)); 
#define ON_OUT_OF_SCOPE(lambda_body) ON_OUT_OF_SCOPE_2(lambda_body, __LINE__) 

FILE * fp = fopen("tmp.txt", "w"); 
ON_OUT_OF_SCOPE({ puts("close file now"); fclose(fp); }); 

Il punto è che si può ottenere una risorsa nel vecchio, modo chiaro e dichiarare la dichiarazione per rilasciare la risorsa immediatamente dopo la linea di acquisizione delle risorse.

Lo svantaggio è che non è possibile inoltrare un singolo oggetto in giro con il suo cancellatore.

Per FILE *, shared_ptr può essere usato come un puntatore alternativa per lo stesso scopo (forse un po 'pesante, ma funziona bene sia per VC2010 e g ++)

shared_ptr FP2 (fopen ("tmp.txt "," w "), [] (FILE * fp) {fclose (fp); puts (" file chiuso ");});

Problemi correlati