2012-05-14 9 views
5

Ho una domanda riguardante la sintassi di posizionamento new in C++. I seguenti due frammenti di codice sono equivalenti dal punto di vista funzionale e possono essere usati in modo intercambiabile (non sto dicendo che il secondo debba essere usato, quando il primo è adatto)?Equivalente comportamento nuovo

# 1

T* myObj = new T(); 
// Do something with myObj 
delete myObj; 

# 2

char* mem = new char[sizeof(T)]; 
T* myObj = new (mem) T(); 
// Do something with myObj 
myObj->~T(); 
delete[] mem; 

C'è qualcosa che dovrebbe essere particolarmente attenti, quando sto usando la nuova sintassi di collocamento come questo?

risposta

11

Non sono equivalenti, perché hanno un comportamento diverso se il costruttore o il distruttore di T genera.

new T() libererà tutta la memoria che è stata allocata prima di far propagare ulteriormente l'eccezione. char* mem = new char[sizeof(T)]; T* myObj = new (mem) T(); non lo farà (e a meno che non facciate esplicitamente qualcosa per assicurarvi che venga liberato avrete una perdita). Allo stesso modo, delete myObj assegnerà sempre la memoria, indipendentemente dal fatto che i lanci siano ~T().

un esatto equivalente per T* myObj = new T();/*other code*/delete myObj; sarebbe qualcosa di simile:

//When using new/delete, T::operator new/delete 
//will be used if it exists. 
//I don't know how do emulate this in 
//a generic way, so this code just uses 
//the global versions of operator new and delete. 
void *mem = ::operator new(sizeof(T)); 
T* myObj; 
try { 
    myObj = new (mem) T(); 
} 
catch(...) { 
    ::operator delete(mem); 
    throw; 
} 
/*other code*/ 
try { 
    myObj->~T(); 
    ::operator delete(mem); 
} 
catch(...) { 
    //yes there are a lot of duplicate ::operator deletes 
    //This is what I get for not using RAII): 
    ::operator delete(mem); 
    throw; 
} 
+0

La memoria allocata da 'new char [sizeof (T)]' sarà allineata correttamente? –

+3

@TadeuszKopec: lo standard garantisce che la memoria abbia il massimo allineamento possibile. Ciò vale solo se si usano allineamenti naturali (cioè, che appaiono con i tipi built-in), se si utilizza la direttiva per eseguire il bump dell'allineamento di un tipo per, diciamo, due volte quello massimo, quindi tutte le scommesse sono disattivate. –

8

Dal momento che stai allocazione di memoria cruda, un equivalente più vicino potrebbe essere:

void *mem = operator new(sizeof(T)); 
T *myobj = new(mem) T(); 

// ... 

myobj->~T(); 
operator delete(mem); 

Si noti che se si è sovraccaricato ::operator new per una particolare classe, questo userà quella classe operator new, dove la vostra utilizzando new char [] lo ignorerei.

Modifica: anche se devo aggiungere che sto ignorando la possibilità di eccezioni qui. @ La risposta di Mankarse sembra (per me) coprire questa parte abbastanza bene.

0

ad un livello fondamentale, si sta facendo la stessa cosa in entrambe le situazioni: cioè si sta un'istanza di un nuovo oggetto sul mucchio e si stai rilasciando la memoria, ma nel secondo caso stai usando (ovviamente) il nuovo operatore di posizionamento.

In questo caso entrambi produrranno gli stessi risultati, meno le differenze se il costruttore lancia come spiegato da Mankarse.

Inoltre, non direi che è possibile utilizzarli in modo intercambiabile, perché il secondo caso non è un esempio realistico di come viene utilizzato placement new. Probabilmente avrebbe molto più senso usare placement new nel contesto di un pool di memoria e se si sta scrivendo il proprio gestore memoria in modo da poter inserire più oggetti T nella memoria per allocata (nei miei test tende a risparmiare circa 25% del tempo di CPU). Se hai un caso d'uso più realistico per placement new, ci saranno molte più cose di cui dovresti preoccuparti.

0

Bene, si sta pre-allocando memoria per il proprio oggetto T. E dovrebbe andare bene. Tuttavia ha senso se riutilizzerai l'area assegnata ancora una volta. Altrimenti sarà più lento.

0

Sì. Il tuo esempio è troppo semplice per dimostrarlo, ma la memoria che hai precedentemente assegnato, "mem", dovrebbe gestire l'oggetto memorizzato all'interno di "myObj."

Forse mettere un modo migliore, lo scenario # 1 alloca spazio sul mucchio, e costruisce un oggetto in quello spazio. Scenario # 2 assegna lo spazio sul mucchio, 'MEM', quindi costruisce un oggetto da qualche parte all'interno di quello spazio.

Ora, mettere un secondo oggetto da qualche altra parte all'interno di "mem." e si complica, giusto?

La costruzione e la distruzione di myObj stanno accadendo in modo identico in entrambi gli scenari (tranne che in il c ase del tuo costruttore che lancia un'eccezione, come indicato da Mankarse), ma l'allocatore si occupa della gestione della memoria per te nello scenario n. 1 e non nello scenario n. 2.

Quindi, fare attenzione a gestire "mem" in modo appropriato. Uno

template<class T> void destroy(T* p, Arena& a) 
{ 
     if (p) { 
       p->~T();  // explicit destructor call 
       a.deallocate(p); 
     } 
}