2013-05-23 14 views
12

Se faccioL'eliminazione di chiamata sul risultato dell'eliminazione di un posizionamento ha utilizzato l'operatore nuovo ok?

struct MyStruct { ~MyStruct() { } }; 

void *buffer = operator new(1024); 
MyStruct *p = new(buffer) MyStruct(); 
// ... 
delete p;  // <---------- is this okay? 

è la delete garantito per prendersi cura di sia chiamando ~MyStruct()così comeoperator delete?

+0

C'è una differenza nella rappresentazione della memoria risultante tra il modo in cui è stato assegnato e costruito l'oggetto rispetto a farlo in un unico passaggio? Se sì, probabilmente l'eliminazione fallirà. Se no, probabilmente l'eliminazione avrà successo. Quindi la domanda si riduce a come lo standard specifica l'operazione di 'new', non' delete'. – nobar

+0

Ho provato questo con gcc + Valgrind e non sono stati segnalati errori. – nobar

risposta

13

delete p è equivalente a

p->~MyStruct(); 
operator delete(p); 

meno MyStruct ha un'alternativa operator delete, così il vostro esempio dovrebbe essere ben definito con la semantica attesi.

[expr.delete]/2 stati:

il valore dell'operando di cancellazione può essere ... un puntatore a un oggetto non array creati da un precedente nuova espressione.

Il nuovo posizionamento è un tipo di nuova espressione.[expr.new]/1:

nuova espressione:
        :: optnuovo nuova collocazione opt nuovo-tipo-id new-inizializzatore opt
        :: optnuovo nuova collocazione opt ( tipo-id) nuova initializer opt

delete è definito come una chiamata al distruttore dell'oggetto e quindi una chiamata alla funzione deallocazione per la memoria. [expr.delete]/6,7:

... il delete-espressione invocherà il distruttore (se presente) per l'oggetto ...

... il delete-espressione chiamerà una funzione deallocazione. ..

finché la funzione di deallocazione corrisponde la funzione di allocazione (che dovrebbe, a patto che non sia sovraccarico operator delete per la classe), allora questo dovrebbe essere ben definito.

+0

+1 per camminare con lo standard! – Yakk

+2

Hmm. Devi anche notare che l'implementazione non è consentita per aggiungere padding dopo il valore di ritorno di 'operator new'. (Non lo è, C++ 11 §5.3.4/10.) Anche così, mi sento ancora a disagio per questo. Non sembra esserci un caso d'uso inevitabile poiché 'dynamic_cast ()' ottiene il puntatore più derivato anche quando non si conosce il tipo dinamico. – Potatoswatter

+0

good point Potatoswatter – GameDeveloper

12

[Revisionato] Generalmente non è OK e generalmente è possibile ottenere solo lo delete che è stato ottenuto con l'espressione corrispondente new. Altrimenti spetta a te garantire che la funzione di deallocazione corrisponda alla funzione di allocazione (quindi utilizzare ::delete p; sarebbe la soluzione generale più sicura, supponendo che il tuo originale operator new fosse nello spazio dei nomi globale). In particolare quando la classe in questione (o una delle sue classi derivate) sovraccaricano operator new devi stare attento.

Dal momento che si sta utilizzando un'espressione posizionamento nuovo per creare *p, e dal momento che non v'è alcuna cosa come un "espressione di collocamento-delete", si deve distruggere l'oggetto manualmente e quindi rilasciare la memoria:

p->~MyStruct(); 

operator delete(buffer); 
+3

A parte l'istintivo "questo è strano e legato alla memoria, quindi deve essere UB", quali prove hai per questo? – Mankarse

+0

@Mankarse: 'delete p' potrebbe invocare' MyStruct :: operator delete', che può fare qualcosa di completamente inappropriato, in cui viene effettivamente richiesto ':: operator delete'. –

+0

Vero, ma ciò non significa che puoi ** solo ** cancellare qualcosa che è stato ottenuto con la semplice espressione 'new'. Può funzionare anche in quelle situazioni in cui si sa che 'MyStruct :: operator delete' non esiste. (Anche se sono d'accordo che questo rende l'uso di 'delete' un po 'pericoloso, e probabilmente lo rende una cattiva idea nella maggior parte delle situazioni). – Mankarse

0

No, in genere non si dovrebbe chiamare delete (in alcuni casi, come quando l'eliminazione dell'operatore è overleaded, potrebbe essere ok).

char *buffer = new char[1024]; 
MyStruct *p = new(buffer) MyStruct(); //placement new "i'm just calling constructor" 
p->~MyStruct(); //destroy the object. 

//delete p; // WHAT? even if you override operator delete it may be opaque to reader. 
delete [] buffer; // THIS DELETE IS OK (if you have first destroyed MyStruct) 
Problemi correlati