2010-09-09 7 views
17

C++ permite la sobrecarga operator new - tanto global como por clase - operator new, operator new[] generalmente utilizado con new[] instrucción y ubicación por separado.¿Cómo podría sobrecargar sensiblemente el operador de colocación nuevo?

Los dos primeros de esos tres suelen estar sobrecargados por utilizar asignadores personalizados y agregar seguimiento. Pero la colocación operator new parece bastante sencilla, en realidad no hace nada dentro. Por ejemplo, en Visual C++ la implementación predeterminada sólo devuelve la dirección pasó a la llamada:

//from new.h 
inline void* operator new(size_t, void* where) 
{ 
    return where; 
} 

qué otra cosa podría hacer? ¿Por qué y cómo podría sobrecargar sensiblemente la ubicación operator new?

+0

¿Está solicitando usos de sobrecarga del nuevo operador? Esa es una pregunta bastante amplia. –

+3

@Alexander Rafferty Estoy preguntando específicamente sobre los usos de la sobrecarga de colocación nueva. No puedo ver ningún propósito en este caso específico. – sharptooth

+0

+1 para acceder a los archivos de encabezado. – Chubsdad

Respuesta

13

La respuesta correcta es no puede reemplazar la ubicación del operador nueva.

§18.4. 1.3 formas de ubicación
Estas funciones están reservadas, un programa C++ no puede definir funciones que desplacen las versiones en la biblioteca estándar de C++.

La razón de ser: El único propósito de los operadores de asignación y desasignación es asignar y desasignar memoria, por lo que cuando se les dé memoria no se debe hacer nada más. (La norma señala específicamente que estas funciones "intencionalmente realizar ninguna otra acción.")

+0

Wow. Visual C++ 9 lo permite felizmente. – sharptooth

+0

@sharptooth: ¿Cuál es su programa de prueba? No es sorprendente, es ilegal de la misma manera que es ilegal agregar cosas (en general) al espacio de nombres 'std'. El lenguaje en sí no es más prudente, pero la biblioteca estándar lo prohíbe. – GManNickG

+0

Reemplacé void * operator new [] (size_t, void * where) yendo a los archivos de encabezado de mi compilador. Funcionó bien En realidad, no lo reemplacé, lo eliminé, para hacerlo inutilizable, porque ** está ** inutilizable. –

3

One example está en Preguntas frecuentes sobre Stroustrup.

+1

Honestamente, no tengo ese ejemplo. – sharptooth

+0

@sharptooth Encontré un ejemplo similar con alguna explicación adicional [aquí] (http://www.parashift.com/c++-faq/memory-pools.html) (aunque hubo algunas ligeras imprecisiones) – blgt

4

Técnicamente, una ubicación operator new es cualquier operator new que tome argumentos adicionales además del tamaño de la memoria necesaria.

Por lo tanto, new(std::nothrow) X utiliza una ubicación operator new y también new(__FILE__, __LINE__) X.

La única razón para anular el operator new(size_t, void*) podría ser agregar información de rastreo, pero creo que la necesidad de eso será bastante baja.

+3

Una razón para implementarlo podría ser forzar el uso de un asignador de memoria particular; esto ha surgido para nosotros cuando realizamos cosas complejas en Windows donde era necesario forzar el uso del asignador utilizado por una DLL en particular en lugar de otra. (Sí, diferentes bibliotecas usaban diferentes asignadores. Todo funcionó, siempre y cuando el código coincida con 'nuevo' de una lib con' delete' de la misma lib.) –

+0

La primera oración es incorrecta, la colocación nueva tiene un significado específico. Y la colocación nueva no se puede sobrecargar ni reemplazar. – GManNickG

+0

@GMan: Tienes razón. No parece haber un nombre específico para las sobrecargas 'operator new' que toman parámetros adicionales, aunque generalmente se invocan con la sintaxis de ubicación. –

-1

Mi uso principal es crear una gran variedad de objetos. Su rendimiento es mucho mejor y tiene menos sobrecarga para asignar la memoria en un bloque completo, es decir, usar VirtualAlloc desde Win32 (cuando se programan ventanas). A continuación, sólo pasa una PTR dentro de ese bloque a cada colocación de objetos nuevos, tales como:

char *cp = new char[totalSize]; 

for(i = 0; i < count; i++, cp += ObjSize)   
{               
    myClass *obj = new(cp) myClass;    
} 
+1

Eso es usar, no sobrecargar ... –

+0

IIUC, él no está solicitando casos de uso del nuevo operador de colocación en sí, sino para casos de uso de * sobrecarga *. –

1

La anulación más obvia sería la de copiar esta aplicación.

Otra opción sensata sería agregar algunas comprobaciones (por ejemplo, verificar que no haya un "marcador de límite" dentro de la zona de solicitud).

Creo que el punto es más de lo que TIENE que anular, tan pronto como anule a los demás (para una clase dada), debido a la mecánica de buscar el nombre (o no anularlo para evitar su uso , eso también está bien, pero es una decisión consciente).

0

He visto un ejemplo en el que se sobrescribía el argumento de dos argumentos nuevo [] para devolver los bloques de memoria llenados con el carácter pasado como el argumento adicional. No recuerdo lo que utiliza el código original (probablemente Memset()), pero era funcionalmente algo como esto:

#include <iostream> 
#include <algorithm> 
#include <new> 
void* operator new [](size_t n, char c) 
{ 
     char* p = new char[n]; 
     std::fill(p, p+n, c); 
     return p; 
} 
int main() 
{ 
     char* p = new('a') char[10]; 
     std::cout << p[0] << p[1] << ".." << p[9] << '\n'; 
} 

aunque supongo que esto no sería llamado "colocación" nueva, ya que no realiza colocación. Probablemente podría ser útil si se modela para que pueda construir matrices de cualquier tipo, llenas con una copia del objeto pasado como su segundo argumento ... pero entonces, tenemos contenedores para eso de todos modos.

+0

Se llama colocación nueva, p. el estándar dice: "Esta sobrecarga se puede aplicar en todas las matrices * nuevas expresiones *, incluidas aquellas que hacen referencia a la función de biblioteca' operador new [] (std :: size_t, void *) '** y otras funciones de asignación de ubicación. * * " –

0

No estoy exactamente seguro de la pregunta, pero la siguiente colocación anulaciones de nuevo a nivel de clase:

struct Bar { 
void* operator new(size_t /* ignored */, void* where) throw() { return where; } 
}; 

int main() { 
    char mem[1]; 
    Bar* bar = new(mem) Bar; 
} 

I cree que esto es legal C++ (y compila y funciona bien con gcc 4.4.6).

Puede cambiar la implementación de este operador como mejor le parezca (incluida la eliminación de la cláusula throw(), lo que significa que el compilador ya no verifica el null del where antes de llamar al constructor). Sin embargo, pisa con cuidado.

§18.4. 1.3 es interesante. Creo que esto solo se aplica a la nueva función del operador global, no a las específicas de la clase.

+1

La pregunta era si alguna otra implementación era posible. ¿Qué más podría hacer excepto 'return where;'? – sharptooth

+0

Ah, ya veo. Bueno, podría verificarlo para asegurarse de que esta dirección no se haya utilizado todavía, que no se superponga con la memoria ya asignada, o incluso podría ajustar 'where' para alinearla mejor (devolviendo' where + n' bytes). Nada de esto que haya visto antes en la práctica. –

0

La extra-funcionalidad más importante para la colocación de nueva sobrecarga sería verificar la alineación de la dirección.

Por ejemplo, supongamos que alguna clase requiere una alineación de 16 bytes. El desarrollador sobrecarga nuevo, nuevo [], eliminar y eliminar [] - solo para asegurarse de que todo esté alineado correctamente.

Todo funciona bien hasta el momento en que trata de utilizar su clase con la biblioteca que usa ubicación nueva ... La biblioteca no tiene idea de si/qué alineación se requiere para la clase y la dirección donde intenta "colocar" el objeto podría no estar alineado - big boom.

El ejemplo más simple de tal situación: intente usar std :: vector <T> donde T requiere alineación no estándar.

La sobrecarga para la colocación nueva permite detectar si el puntero no está alineado, lo que podría ahorrar horas de depuración.

1

Definir su propia gestión de memoria para un área reservada es un buen uso.

Tener diferentes vistas en los mismos datos físicos (sin necesidad de mover los datos) es otro uso de interconexión. También le permite leer un archivo estructurado como caracteres en un búfer y luego, la superposición de su estructura lógica definiendo un objeto de esa clase sobre el búfer. La combinación de esto con el mapeo de memoria de archivos puede proporcionar grandes mejoras en el rendimiento. El hardware asignado a la memoria ... ¡Así que miles de aplicaciones!

+1

¿Podría darnos un ejemplo? Esta pregunta no se trata de utilizar la ubicación nueva del código de usuario, se trata de sobrecargar al operador. – sharptooth

Cuestiones relacionadas