2012-04-18 23 views
8

He estado buscando asignadores personalizados y con bastante frecuencia los veo usando algún tipo de función para asignar memoria. Para propósitos de prueba y para educarme más a mí mismo, traté de hacer un ejemplo "simple" de hacerlo. Sin embargo, hay una cosa fundamental que entiendo sobre cómo hacer. Una de las diferencias clave en malloc frente a new es que con el nuevo se llama al constructor. ¿Qué pasaría si quisiera escribir mi propio asignador que esencialmente reemplazara new, cómo haría que se llame al constructor cuando use malloc?Callling object constructor/destructor con un asignador personalizado

Entiendo que en las clases puedo sobrecargar new y delete para la clase, así que supongo que una gran parte de la pregunta es, ¿cómo llama el new al constructor de objetos durante la asignación? Del mismo modo, estoy interesado en cómo delete llama al destructor.

Creé un código de prueba de muestra que esperaba que llamara el constructor SomeClass durante la asignación, pero no veo cómo.

#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; 
} 

(Como nota, sé que sólo puede utilizar new. Sin embargo, para los propósitos de aprendizaje Estoy intentando crear un asignador de costumbre que no sólo llamar new o placement new).

Respuesta

10

En esencia, cuando se utiliza una nueva expresión como: T *t = new T;, que es más o menos equivalente a:

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

Así, primero se asigna una cierta memoria cruda usando la función de asignación, entonces construye un objeto en esa memoria. Del mismo modo, cuando se utiliza una expresión de borrado como: delete t;, que es más o menos equivalente a:

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

Por lo tanto, si sobrecarga new y delete para una clase particular:

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); 
    } 
}; 

A continuación, cuando se utiliza un nuevo expresión, invocará la clase 'operator new para asignar la memoria, y que llamará a malloc, por lo que T *t = new T(); terminará asignando memoria a través de malloc (y del mismo modo, cuando delete, utilizará operator delete, que wi ll llama al free).

Al menos como el término se usa normalmente, un Allocator es bastante similar, excepto que es utilizado por un contenedor en lugar de otro código. También encapsula la función de asignación y la función de eliminación en una clase, por lo que cuando pasa uno al contenedor, solo tiene que pasar un objeto, y hay pocas posibilidades de que una función de asignación y eliminación no coincida.

Haciendo caso omiso, por el momento, los detalles acerca de qué nombres se utilizan para las cosas, la clase Allocator en la biblioteca estándar mayoría hace lo mismo, así que con un poco de cambio de nombre de las funciones en la clase T anterior, se' Estar a la mitad escribiendo un asignador estándar. Para ir con la asignación y eliminación, tiene una función para rebind algo de memoria (cambiar un bloque de memoria a otro tipo), crear un objeto en su lugar (básicamente solo un contenedor alrededor de una ubicación nueva) y destruir un objeto (de nuevo, trivial) envoltorio alrededor de la invocación del destructor). Por supuesto, usa operator new y operator delete en lugar de malloc y free como he usado anteriormente.

+0

Si la ubicación nueva es lo que necesito usar, ¿cómo es que, por ejemplo,' new' de MSVC no llama a la ubicación nueva en su versión de 'new' (en new.cpp, puede obtenerla entrando en una nueva llamada) – mmurphy

+0

@mmurphy: * Aproximadamente * similar a, definitivamente no idéntico a. –

7

Con una ubicación nueva puede pasar una ubicación de memoria ya asignada al nuevo operador. Entonces, new construirá el objeto en el lugar dado sin realizar una asignación sobre sí mismo.

Editar:

Ésta es la forma en que se podría implementar:

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

El cartel dijo que no quiere llamar a nuevo. – josephthomas

+4

Los constructores no tienen nombre (se oponen a los destructores) por lo que no se pueden llamar directamente. Una ubicación nueva es la única forma de construir correctamente una instancia de clase si desea que ocupe una ubicación de memoria determinada. – bjhend

+2

Pregunté específicamente sin usar nuevo. – mmurphy

0

para obtener el constructor a ser llamados usar la colocación nueva (Tenga en cuenta que no se puede anular la colocación de nuevo). Para eliminar y todas las trampas así the FAQ does a good job of explaining it.

+1

específicamente preguntado sin usar new. – mmurphy

+0

Sí, pero la colocación nueva es una primitiva del lenguaje, no hay forma de implementarla en el idioma. – stonemetal

+1

@stonemetal: la ubicación nueva no es una primitiva del lenguaje, es decir, el operador 'void * new (tamaño_t bytes, void * place) {return place; } ' – ulatekh

Cuestiones relacionadas