2010-12-11 9 views
12

Sono sicuro che questo codice dovrebbe essere illegale, in quanto chiaramente non funzionerà, ma sembra essere consentito dal FCD C++ 0x.Legalità dell'utilizzo dell'eliminazione dell'operatore su un puntatore ottenuto dal posizionamento nuovo

class X { /* ... */}; 
void* raw = malloc(sizeof (X)); 
X* p = new (raw) X(); // according to the standard, the RHS is a placement-new expression 
::operator delete(p); // definitely wrong, per litb's answer 
delete p; // legal? I hope not 

Forse uno di voi avvocati della lingua può spiegare come lo standard lo proibisce.

C'è anche una forma di matrice:

class X { /* ... */}; 
void* raw = malloc(sizeof (X)); 
X* p = new (raw) X[1]; // according to the standard, the RHS is a placement-new expression 
::operator delete[](p); // definitely wrong, per litb's answer 
delete [] p; // legal? I hope not 

This is the closest question sono stato in grado di trovare.

EDIT: Io non sono solo di acquistare l'argomento che gli argomenti lingua restrittivi della norma per funzionare void ::operator delete(void*) applicare in modo significativo per l'operando di delete in un delete-espressione. Nella migliore delle ipotesi, la connessione tra i due è estremamente tenue, e un certo numero di espressioni sono consentito come operandi delete che non sono validi per passare a void ::operator delete(void*). Ad esempio:

struct A 
{ 
    virtual ~A() {} 
}; 

struct B1 : virtual A {}; 

struct B2 : virtual A {}; 

struct B3 : virtual A {}; 

struct D : virtual B1, virtual B2, virtual B3 {}; 

struct E : virtual B3, virtual D {}; 

int main(void) 
{ 
    B3* p = new E(); 
    void* raw = malloc(sizeof (D)); 
    B3* p2 = new (raw) D(); 

    ::operator delete(p); // definitely UB 
    delete p; // definitely legal 

    ::operator delete(p2); // definitely UB 
    delete p2; // ??? 

    return 0; 
} 

Spero questo dimostra che se un puntatore può essere passato a void operator delete(void*) non serve a stabilire se stesso puntatore può essere utilizzato come operando di delete.

+1

FYI: L'FCD (N3092) non è più l'ultima bozza. L'ultima bozza è N3225. Ho mantenuto la [pagina wiki del tag C++ - 0x] (http://stackoverflow.com/tags/c%2b%2b0x/info) aggiornata con un link all'ultima bozza di PDF. –

+1

Si noti che 5.3.5/2, che copre questo, è stato modificato nell'ultima bozza. Ora dice che il puntatore potrebbe essere "un puntatore a un oggetto non matrice creato da una precedente _new-espressione_", e una _new-espressione_ include effettivamente nuove espressioni di posizionamento. Non penso che sia inteso. –

+0

@James: Grazie MOLTO tanto per la nuova bozza. E 5.3.5 è esattamente la sezione che sto pensando dovrebbe proibire, ma non lo fa. Potresti per favore guardare la mia risposta (mi sto preparando per inserire qualsiasi lingua modificata dalla nuova bozza) e fammi sapere se pensi che abbia qualche relazione con questa domanda? –

risposta

2

Il compilatore non si preoccupa veramente che p provenga da una chiamata new, pertanto non impedirà l'emissione di delete sull'oggetto. In questo modo, il tuo esempio può essere considerato "legale".

Tuttavia, ciò non funziona, dal momento che gli operatori "posizionamento delete" non possono essere chiamati esplicitamente. Vengono chiamati solo implicitamente se il costruttore lancia, quindi il distruttore può essere eseguito.

2

Suppongo che si potrebbe ottenere via con esso (su un particolare compilatore) se

  1. new/delete sono implementate in termini di malloc/free e
  2. il posizionamento new in realtà utilizza lo stesso meccanismo per mantenere traccia del distruttore associato con allocazioni come standard new, in modo che la chiamata a delete potrebbe trovare il distruttore giusto.

Ma non c'è modo che possa essere portatile o sicuro.

+1

Non è sicuramente sicuro, perché quell'espressione di eliminazione è necessaria per chiamare una funzione di deallocazione, passando un puntatore che non proviene dalla funzione di allocazione corrispondente. –

2

ho trovato nella sezione libreria dello standard, che è quanto intuitivo una località (IMO) possibile:

C++ 0x FCD (e tiraggio n3225) sezione 18.6.1.3, [new.delete.placement] :

Queste funzioni sono riservate, un programma C++ non può definire le funzioni che spostano le versioni in standard libreria C++ (17.6.3). Le disposizioni di (3.7.4) non si applicano a questi moduli di inserimento riservati dell'operatore nuovo e all'eliminazione dell'operatore.

void* operator new(std::size_t size, void* ptr) throw(); 
void* operator new[](std::size_t size, void* ptr) throw(); 
void operator delete(void* ptr, void*) throw(); 
void operator delete[](void* ptr, void*) throw(); 

Eppure, la sezione di definizione delle espressioni legali per passare a delete è 5.3.5, non è 3.7.4.

7

le regole standard a [basic.stc.dynamic.deallocation] p3

In caso contrario, il valore fornito al operator delete(void*) nella libreria standard deve essere uno dei valori restituiti da una precedente invocazione sia operator new(size_t) o operator new(size_t, const std::nothrow_t&) nella libreria standard e il valore fornito a operator delete[](void*) nella libreria standard deve essere uno dei valori restituiti da una precedente chiamata di operator new[](size_t) o operator new[](size_t, const std::nothrow_t&) nella libreria standard.

tuo delete chiamata chiamerà il librerie operator delete(void*), se non si ha sovrascritto esso. Dal momento che non hai detto nulla al riguardo, assumerò che tu non l'abbia fatto.

Il "deve" sopra in realtà dovrebbe essere qualcosa come "il comportamento non è definito se non", quindi non è errato come una regola diagnosticabile, che non è da [lib.res.on.arguments] p1. Questo è stato corretto da n3225 quindi non può più essere confuso.

+0

Questo vale sicuramente per chiamare le funzioni di deallocazione direttamente usando la sintassi di chiamata di funzione. Ma non penso che sia immediatamente applicabile alle invocazioni di 'operator delete', poiché il puntatore che uso come operando di' operator delete' non è necessariamente quello che alla fine viene passato alla funzione deallocation. Bene, leggendo 5.3.4 '[expr.new]' più da vicino, è lo stesso per il nuovo scalare. Quindi pensa in termini di posizionamento di array, invece, seguito da array delete. –

+0

@ Ben Non capisco il tuo commento. –

+0

Il compilatore dovrebbe essere responsabile di seguire le regole per le funzioni di deallocation, a patto che seguo le regole per * new-expression * e * delete-expression *. Che purtroppo questo codice fa, secondo l'attuale formulazione citata da James. –