2012-04-18 16 views
8

Ho esaminato gli allocatori personalizzati e molto spesso li vedo usando un qualche tipo di funzione per allocare memoria. Per scopi di test e per istruirmi ulteriormente, ho cercato di fare un esempio "semplice" di farlo. Tuttavia, c'è una cosa fondamentale che ho capito su come fare. Una delle principali differenze in malloc rispetto a new è che con il nuovo viene chiamato il costruttore. Cosa succede se volessi scrivere il mio allocatore che essenzialmente sostituisce new, come potrei chiamare il costruttore quando si utilizza malloc?Callling object constructor/destructor con un allocatore personalizzato

Capisco che sulle classi posso sovraccaricare new e delete per la classe, quindi suppongo che una grande parte della domanda è: come si new chiamando il costruttore di oggetti durante l'allocazione? Allo stesso modo, sono interessato a come delete chiama il distruttore.

Ho creato un codice di prova di esempio che speravo di avere il costruttore SomeClass chiamato durante l'allocazione, ma non vedo come.

#include <malloc.h> 

void* SomeAllocationFunction(size_t size) { 
    return malloc(size); 
} 

class SomeClass 
{ 
public: 
    SomeClass() { 
     int con = 1000; 
    } 

    ~SomeClass() { 
     int des = 80; 
    } 
}; 

int main(void){ 
    SomeClass* t = (SomeClass*)SomeAllocationFunction(sizeof(SomeClass)); 
    return 0; 
} 

(Come nota, so che posso solo usare new. Tuttavia, ai fini di un apprendimento Sto cercando di creare un allocatore personalizzato che non si limita a chiamare new o placement new).

risposta

10

In sostanza, quando si utilizza una nuova espressione del tipo: T *t = new T;, è grosso modo equivalente a:

void *temp = operator new(sizeof(T)); 
T *t = new(temp) T; 

Quindi, prima si alloca della memoria grezzo utilizzando la funzione di allocazione, quindi costruisce un oggetto in quella memoria. Allo stesso modo, quando si utilizza un'espressione di eliminazione come: delete t;, è grosso modo equivalente a:

t->~T(); 
operator delete(t); 

Quindi, se si esegue l'overload new e delete per una particolare classe:

class T { 
    int data; 
public: 
    // I've made these static explicitly, but they'll be static even if you don't. 
    static void *operator new(size_t size) { 
     return malloc(size); 
    } 
    static void operator delete(void *block) { 
     free(block); 
    } 
}; 

Poi, quando si utilizza un nuovo espressione, invocherà la classe 'operator new per allocare la memoria, che chiamerà malloc, quindi T *t = new T(); finirà per allocare memoria tramite malloc (e allo stesso modo, quando si utilizza , verrà utilizzato operator delete, che verrà utilizzato ll chiamare free).

Almeno come il termine è usato normalmente, un Allocatore è abbastanza simile, tranne che è usato da un contenitore al posto di altro codice. Incapsula anche la funzione di allocazione e la funzione di cancellazione in una classe, quindi quando ne passi una al contenitore, devi solo passare un oggetto, e ci sono poche possibilità che una funzione di allocazione e di eliminazione non corrisponda.

Ignorando, per il momento, i dettagli su quali nomi sono usati per le cose, la classe Allocator nella libreria standard principalmente fa lo stesso, quindi con un po 'di ridenominazione delle funzioni nella classe T sopra, si' d essere circa la metà di aver scritto un allocatore standard. Per andare con l'allocazione e la cancellazione, ha una funzione di memoria rebind (cambia un blocco di memoria in un altro tipo), crea un oggetto in posizione (fondamentalmente solo un involucro attorno a un posizionamento nuovo) e distrugge un oggetto (di nuovo, banale involucro attorno all'invocazione del distruttore). Certamente, utilizza operator new e operator delete invece di malloc e free come ho usato sopra.

+0

Se il posizionamento è quello che devo usare, allora come ad esempio' new' di MSVC non chiama il nuovo posizionamento nella sua versione di 'new' (in new.cpp, puoi ottenerlo entrando in una nuova chiamata). – mmurphy

+0

@mmurphy: * Approssimativamente * simile a, sicuramente non identico a. –

7

Con un nuovo inserimento è possibile passare una posizione di memoria già assegnata al nuovo operatore. Quindi new costruirà l'oggetto nel punto indicato senza fare un'assegnazione su se stesso.

Edit:

Ecco come potrebbe essere attuato:

int main(void){ 
    // get memory 
    void * mem_t = SomeAllocationFunction(sizeof(SomeClass)); 
    // construct instance 
    SomeClass* t = new(mem_t) SomeClass; 

    // more code 

    // clean up instance 
    t->~SomeClass(); 
    return 0; 
} 
+2

Il poster ha detto che non vuole chiamare nuovo. – josephthomas

+4

I costruttori non hanno un nome (contrario ai distruttori) quindi non possono essere chiamati direttamente. Un nuovo posizionamento è l'unico modo per costruire correttamente un'istanza di classe se si desidera che occupi una determinata posizione di memoria. – bjhend

+2

Ho chiesto esplicitamente di usare new. – mmurphy

0

per ottenere il costruttore per essere chiamato di utilizzare nuova collocazione (Nota, non è possibile ignorare nuova collocazione). Per cancellare e tutti i trucchi e the FAQ does a good job of explaining it.

+1

Richiesto espressamente senza utilizzare new. – mmurphy

+0

Sì, ma il posizionamento nuovo è un primitivo linguistico, non è possibile implementarlo nella lingua. – stonemetal

+1

@stonemetal: Placement-new non è una primitiva linguistica, ad esempio 'void * operator new (size_t byte, void * place) {return place; } ' – ulatekh

Problemi correlati