2013-06-30 7 views
9

Nel suo nuovo libro TC++PL4, Stroustrup getta una luce leggermente diversa sul a once usual practiceregarding user-controlled memory allocation and placement new — o, più specificamente, per quanto riguarda l'enigmatica "posizionamento delete." Nella setta del libro. 11.2.4, Stroustrup scrive:forme di collocamento del l'operatore delete funzioni

Il "posizionamento delete" Gli operatori non fanno nulla tranne forse informare un garbage collector che il puntatore eliminata è derivato non è più al sicuro.

Ciò implica che la pratica di programmazione del suono seguirà una chiamata esplicita a un distruttore entro a call to placement delete.

Abbastanza giusto. Tuttavia, non v'è alcuna sintassi meglio chiamare il posizionamento delete che l'oscuro

::operator delete(p); 

Il motivo che mi chiedo è quella setta di Stroustrup. 11.2.4 non menziona tale sintassi dispari. In effetti, Stroustrup non si sofferma sulla questione; non parla affatto di sintassi. Non mi piace affatto l'aspetto di ::operator, che interpella la questione della risoluzione dello spazio dei nomi in qualcosa che non ha proprio niente a che fare con i namespace. Non esiste una sintassi più elegante?

Per riferimento, ecco la citazione di Stroustrup nel contesto più piena:

Per impostazione predefinita, l'operatore new crea il suo oggetto sul negozio libero. ? Cosa se volessimo l'oggetto allocato altrove ... Siamo in grado di collocare oggetti ovunque, fornendo una funzione allocatore con argomenti extra e poi forniscono tali argomenti extra quando si utilizza new:

void* operator new(size_t, void* p) { return p; } 

void buf = reinterpret_cast<void*>(0xF00F); 
X* p2 = new(buf) X; 

A causa di questo utilizzo, la sintassi new(buf) X per la fornitura di ulteriori argomenti a operator new() è nota come sintassi del posizionamento . Si noti che ogni operator new() assume una dimensione come primo argomento e che la dimensione dell'oggetto assegnato è implicitamente fornita. L'operator new() utilizzato dall'operatore new viene scelto dalle normali regole di corrispondenza degli argomenti ; ogni operator new() ha a size_t come primo argomento.

Il "posizionamento" operator new() è il più semplice di questi allocatori. E è definita nella intestazione standard <new>:

void* operator new (size_t, void* p) noexcept; 
void* operator new[](size_t, void* p) noexcept; 

void* operator delete (void* p, void*) noexcept; // if (p) make *p invalid 
void* operator delete[](void* p, void*) noexcept; 

Il "posizionamento delete" operatori non fanno nulla tranne forse informare un garbage collector che il puntatore eliminata non è più al sicuro derivata.

Stroustrup continua quindi a discutere dell'uso del posizionamento new con arene. Non sembra menzionare ancora il posizionamento delete.

+2

Puoi fornire un preventivo più completo? Potresti confondere una * funzione * che si chiama "operatore" con un operatore * effettivo *. Non è presente l'espressione delete-delete * in C++. –

+2

C'è un solo tipo di "delete expression" in C, e non ammette alcuna sintassi di posizionamento (basta guardare le produzioni di grammatica nello standard). Questo è ciò che molti programmatori C++ ragionevolmente esperti significano quando dicono "non esiste * espressione delete di posizionamento * in C++". Forse ci sarà qualcosa in questo senso in C++ 14 o C++ 17, ma ora non c'è niente. –

+0

@KerrekSB: ora ho dato una citazione più completa. – thb

risposta

2

Se non si desidera utilizzare ::, non è necessario. In effetti, generalmente non dovresti (non voglio).

È possibile fornire sostituzioni per ::operator new e ::operator delete (e le varianti di array, sebbene non si debbano mai usarle).

È anche possibile, però, sovraccaricare operator new e operator delete per una classe (e sì, ancora una volta, si possono fare le varianti di array, ma ancora non dovrebbe mai usarli).

L'utilizzo di qualcosa come void *x = ::operator new(some_size); impone l'allocazione per passare direttamente a operator new globale anziché utilizzare uno specifico di classe (se esistente). Generalmente, ovviamente, vuoi usare quello specifico della classe se esiste (e quello globale se non lo è). Questo è esattamente ciò che ottieni usando void *x = operator new(some_size); (cioè, nessun operatore di risoluzione scope).

Come sempre, è necessario assicurarsi che i vostri new s e delete s partita, così si dovrebbe usare solo ::operator delete cancellare la memoria quando/se è stato utilizzato ::operator new allocare. Il più delle volte non dovresti usare :: su nessuno dei due.

L'eccezione principale a questo è quando/se in realtà stai scrivendo uno operator new e operator delete per qualche classe. Questi in genere chiamano ::operator new per ottenere una grande quantità di memoria, quindi dividerla in pezzi di dimensioni di oggetti. Per allocare quel grosso pezzo di memoria, tipicamente (sempre?) Deve specificare esplicitamente ::operator new perché altrimenti finirebbe per chiamare se stesso per allocarlo. Ovviamente, se specifica ::operator new quando assegna i dati, è necessario specificare anche ::operator delete corrispondente.

2

Prima di tutto: No, non c'è.

Ma qual è il tipo di memoria? Esattamente, non ce l'ha. Allora perché non utilizzare la seguente:

typedef unsigned char byte; 

byte *buffer = new byte[SIZE]; 

Object *obj1 = new (buffer) Object; 
Object *obj2 = new (buffer + sizeof(Object)) Object; 
... 
obj1->~Object(); 
obj2->~Object(); 

delete[] buffer; 

In questo modo non c'è bisogno di preoccuparsi di posizionamento eliminare a tutti. Basta avvolgere il tutto in una classe chiamata Buffer ed ecco fatto.

EDIT

ho pensato alla tua domanda e provato un sacco di cose, ma non ho trovato occasione di quello che si chiama placement delete. Quando dai un'occhiata all'intestazione <new> vedrai che questa funzione è vuota. Direi che è solo lì per motivi di completezza. Anche quando usi i modelli puoi chiamare manualmente il distruttore, sai?

class Buffer 
{ 
    private: 
     size_t size, pos; 
     byte *memory; 

    public: 
     Buffer(size_t size) : size(size), pos(0), memory(new byte[size]) {} 

     ~Buffer() 
     { 
      delete[] memory; 
     } 

     template<class T> 
     T* create() 
     { 
      if(pos + sizeof(T) > size) return NULL; 
      T *obj = new (memory + pos) T; 
      pos += sizeof(T); 
      return obj; 
     } 

     template<class T> 
     void destroy(T *obj) 
     { 
      if(obj) obj->~T(); //no need for placement delete here 
     } 
}; 


int main() 
{ 
    Buffer buffer(1024 * 1024); 

    HeavyA *aObj = buffer.create<HeavyA>(); 
    HeavyB *bObj = buffer.create<HeavyB>(); 

    if(aObj && bObj) 
    { 
     ... 
    } 

    buffer.destroy(aObj); 
    buffer.destroy(bObj); 
} 

Questa classe è solo un'arena (come lo chiama Stroustrup). Puoi usarlo quando devi allocare molti oggetti e non vuoi che il sovraccarico di chiamare ogni volta sia nuovo. IMHO questo è il solo caso d'uso per un posizionamento nuovo/cancella.

+0

Hai ragione. Questo è più o meno quello che ho fatto. Quello che volevo imparare oggi è ciò che Stroustrup intende quando sembra suggerire che io e te iniziamo a farlo in modo diverso. – thb

2

Ciò implica che la pratica di programmazione del suono seguirà una chiamata esplicita a un distruttore da una chiamata all'eliminazione del posizionamento.

No, non è così.IIUC Stroustrup non significa piazzamento delete è necessario per informare il garbage collector che la memoria non è più in uso, vuol dire che non fa nulla oltre a quello. Tutte le funzioni di deallocation possono indicare che la memoria di un garbage colector non è più utilizzata, ma quando si usa il posizionamento new per gestire la memoria da soli, perché si vorrebbe che un garbage collector giochini comunque con quella memoria?

ho vagamente non amano il look di ::operator, che interviene la questione della risoluzione dello spazio dei nomi in qualcosa che non ha nulla di particolarmente correttamente a che fare con gli spazi dei nomi.

"correttamente" si fa hanno a che fare con gli spazi dei nomi, qualificandosi per riferirsi al "globale operator new" la distingue da qualsiasi sovraccarico operator new per i tipi di classe.

Non esiste più una sintassi elegante?

Probabilmente non lo vorrete mai chiamare. Un operatore di posizionamento delete si chiamerà dal compilatore se si utilizza il posizionamento new e il costruttore genera un'eccezione. Dal momento che non c'è memoria da deallocare (poiché il ritmo new non ne ha allocati) tutto ciò che è potenzialmente contrassegna la memoria come inutilizzata.