¿Por qué se sustituye por defecto de nuevo y eliminar a los operadores?

Por qué debe uno de ellos tendría que reemplazar el operador predeterminado new y delete con una costumbre new y delete operadores?

Esto es en la continuación de Sobrecarga de nuevo y eliminar en el inmensamente iluminando C++ FAQ:

Sobrecarga de operadores.

Un seguimiento de la entrada a esta sección de preguntas frecuentes es:

¿Cómo debo escribir ISO C++ estándar compatible personalizado nuevo y eliminar operadores?


Nota: La respuesta se basa en las lecciones de Scott Meyers Más Eficaz de C++.

(Nota: Esto pretende ser una puerta de entrada a Desbordamiento de pila en C++ FAQ. Si quieres una crítica a la idea de proporcionar un documento de preguntas frecuentes en este formulario, a continuación la publicación en el meta, la que comenzó todo esto sería el lugar para hacerlo. Las respuestas a esta pregunta son monitoreados en el C++ sala de chat, donde el FAQ idea que comenzó en el primer lugar, entonces, la respuesta es muy probable que se lea por aquellos a quien se le ocurrió la idea).

InformationsquelleAutor Alok Save | 2011-08-22

7 Kommentare

  1. 66

    Uno puede intentar sustituir new y delete operadores para un número de razones, a saber:

    Para Detectar Errores De Uso:

    Hay un número de maneras en las que el uso incorrecto de new y delete puede conducir a la temida bestias de un Comportamiento Indefinido & pérdidas de Memoria.
    Respectivos ejemplos de cada uno son:

    El uso de más de un delete en newed memoria & no llamar delete en la memoria asignada mediante new.

    Un operador sobrecargado new pueden mantener una lista de asignación de direcciones y el operador sobrecargado delete puede eliminar direcciones de la lista, entonces es fácil detectar este tipo de errores de uso.

    Del mismo modo, una variedad de errores de programación pueden llevar a las saturaciones de datos(la escritura más allá del final de un bloque asignado) y diferencias(escrito antes del comienzo de un bloque asignado).

    Un operador Sobrecargado new puede asignar bloques y poner conocido patrones de bytes («firmas») antes y después de la memoria disponible para los clientes. El operador sobrecargado elimina puede comprobar para ver si las firmas están todavía intactas.
    Por lo tanto, mediante la comprobación de si estas firmas no están intactos es posible determinar que una saturación o la sub-ejecución ocurrió en algún momento durante la vida del bloque asignado, y el operador puede eliminar del registro de ese hecho, junto con el valor de los infractores puntero, lo que ayuda en la prestación de un buen diagnóstico de la información.


    Para Mejorar la Eficiencia de la velocidad (& memoria):

    La new y delete operadores que funcionan razonablemente bien para todo el mundo, pero de manera óptima para nadie. Este comportamiento se deriva del hecho de que están diseñados para el uso de fines generales solamente. Que tienen que adaptarse a la asignación de patrones que van desde la asignación dinámica de un par de bloques que existen para la duración del programa a la constante asignación y desasignación de un gran número de objetos de corta duración. Finalmente, el operador new y operador delete que se suministran con los compiladores de tomar un medio-de-la-carretera de la estrategia.

    Si usted tiene una buena comprensión de su programa de memoria dinámica de los patrones de uso, usted puede encontrar a menudo que las versiones personalizadas de operador new y delete de operador de superar (más rápido en el rendimiento, o requieren menos memoria de hasta el 50%)de la fábrica. Por supuesto, a menos que esté seguro de lo que está haciendo no es una buena idea hacer esto(ni siquiera lo intentes esto si usted no entiende las complejidades involucradas).


    Para Recopilar Estadísticas De Uso:

    Antes de pensar en la sustitución de new y delete para la mejora de la eficiencia como se mencionó en el #2, Usted debe reunir información acerca de cómo su aplicación/programa utiliza la asignación dinámica. Puede que desee para recoger información sobre:

    Distribución de los bloques de asignación

    La distribución de tiempos de vida,

    El orden de las asignaciones(FIFO o LIFO o al azar),

    La comprensión de los patrones de uso de los cambios durante un período de tiempo,la cantidad máxima de memoria dinámica usada etc.

    También, a veces usted puede necesitar para recopilar información de uso, tales como:

    Contar el número de dinámicamente los objetos de una clase,

    Restringir el número de objetos se crean mediante la asignación dinámica de la etc.

    Todos, esta información puede ser recogida mediante la sustitución de la costumbre new y delete y agregar el diagnóstico de colección en el mecanismo de sobrecarga new y delete.


    Para compensar subóptima de memoria la alineación en new:

    Muchas arquitecturas de computadora requieren que los datos de tipos particulares de ser colocado en la memoria en determinados tipos de direcciones. Por ejemplo, una arquitectura podría requerir que los punteros se producen en las direcciones que son un múltiplo de cuatro (es decir, ser de cuatro bytes alineados) o que duplica debe ocurrir en direcciones que son un múltiplo de ocho (es decir, ser de ocho bytes alineados). Incumplimiento de dichas restricciones pueden conducir a hardware excepciones en tiempo de ejecución. Otras arquitecturas son más indulgentes, y puede permitir que el trabajo a pesar de la reducción del rendimiento.El operador new que se suministran con algunos compiladores no es garantía de ocho bytes de alineación dinámica
    las asignaciones de dobles. En tales casos, la sustitución del operador predeterminado new con uno que garantice ocho bytes de alineación podría producir grandes incrementos en el rendimiento de los programas y aplicaciones; puede ser una buena razón para reemplazar new y delete operadores.


    Para clúster de objetos relacionados con cerca una de la otra:

    Si usted sabe que en particular las estructuras de datos son generalmente usados juntos y que te gustaría para minimizar la frecuencia de fallos de página cuando se trabaja sobre los datos, puede tener sentido para crear un montón independiente de las estructuras de datos por lo que se agrupan juntos en tan pocas páginas como sea posible. personalizado de la Colocación de las versiones de new y delete puede hacer que sea posible para lograr dicha agrupación.


    Para obtener no convencionales de comportamiento:

    A veces se desea que los operadores new y delete para hacer algo que el compilador siempre de las versiones no ofrecen.

    Por ejemplo: Usted podría escribir una costumbre operador delete que sobrescribe se cancela la asignación de memoria con ceros en el fin de aumentar la seguridad de los datos de la aplicación.

    • Yo no estoy en contra de FAQ preguntas, pero si usted tiene la intención de proporcionar uno, sea cuidadoso y correcto, y es capaz de apoyar su reclamo. El operador nuevo que se suministran con algunos compiladores no es garantía de ocho bytes de alineación para asignaciones dinámicas de dobles.? El estándar de C++ §18.4.1.1/1 sobre operator new los estados de que se trata de La función de asignación (3.7.3.1) llamado por una nueva expresión (5.3.4) para asignar el tamaño de bytes de almacenamiento convenientemente alineados para representar cualquier objeto de ese tamaño.
    • Otro es con respecto a la eficiencia, mientras que algunos de los artículos afirman que el rendimiento de un producto artesanal de asignador podría ser mejor que el de fábrica, existen otros estudios que afirman lo contrario. Decirle a la gente que se cree más rápido de la asignación de funciones que los proporcionados por el sistema es, pues, la mayoría de nosotros no lo hará mejor que la disponible y podría introducir errores.
    • Rodríguez – dribeas: Gracias por tu comentario, se me ocurrió la respuesta basada en mi aprendizaje y ni siquiera voy a reclamar lo que es mejor o correcto para el core & esa es la razón de la gente en el C++ Salón, Luc Danton ser específico es la revisión de la misma(por ahora) y vendrá con una lista de puntos de revisión, que vamos a editar aquí, o añadir como de respuestas separada.
    • No muchas de esas razones son válidas para la sustitución de la (global) por defecto de los operadores, pero posiblemente por el uso de alguna clase específica de las sobrecargas de los operadores. Suponiendo que todos podemos generalmente lo hacen mejor que la aplicación es un poco, no sé…
  2. 12

    Primero de todo, hay realmente un número de diferentes new y delete operadores (un número arbitrario, la verdad).

    Primero, hay ::operator new, ::operator new[], ::operator delete y ::operator delete[]. En segundo lugar, para cualquier clase de X, hay X::operator new, X::operator new[], X::operator delete y X::operator delete[].

    Entre estos, es mucho más común de la sobrecarga de la clase específica de los operadores de los operadores globales — es bastante común el uso de la memoria de una clase en particular que siguen un determinado patrón suficiente que usted puede escribir los operadores que ofrecen mejoras sustanciales sobre los valores predeterminados. Por lo general es mucho más difícil predecir el uso de memoria casi que con precisión o, más específicamente, sobre una base global.

    Probablemente también vale la pena mencionar que aunque operator new y operator new[] están separados el uno del otro (del mismo modo que para cualquier X::operator new y X::operator new[]), no hay ninguna diferencia entre los requisitos para los dos. Uno será invocada para asignar un único objeto, y el otro para asignar una matriz de objetos, pero cada uno sólo recibe una cantidad de memoria necesaria, y debe devolver la dirección de un bloque de memoria (al menos) de que los grandes.

    Hablando de los requisitos, es probable que valga la pena revisar los demás requisitos1: el global de los operadores debe ser verdaderamente global — usted no puede poner uno dentro de un espacio de nombres o hacer uno estático en una determinada unidad de traducción. En otras palabras, sólo hay dos niveles en los que las sobrecargas pueden tener lugar: una clase específica de sobrecarga o de un mundial de sobrecarga. Entre los puntos tales como «todas las clases del espacio de nombres de X» o «todas las asignaciones en la unidad de traducción, Y» no están permitidos. La clase específica de los operadores están obligados a ser static – pero usted no está obligado a declarar como estático-que se ser estático si se declara explícitamente en ellos static o no. Oficialmente, los operadores globales el rendimiento de la memoria alineados de modo que pueda ser utilizado para un objeto de cualquier tipo. Extraoficialmente, hay un pequeño margen de acción en un aspecto: si usted recibe una solicitud de un pequeño bloque (por ejemplo, 2 bytes) usted realmente sólo necesita proporcionar a la memoria alineados para un objeto hasta que el tamaño, ya que intenta almacenar nada más habría de conducir a un comportamiento indefinido de todos modos.

    Haber cubierto los preliminares, volvamos a la pregunta original sobre por qué te gustaría sobrecarga de estos operadores. En primer lugar, debo señalar que las razones para la sobrecarga de los operadores globales tienden a ser sustancialmente diferentes de las razones por la sobrecarga de la clase operadores específicos.

    Ya que es más común, voy a hablar de la clase específica de los operadores. La razón principal para la clase específica de la gestión de la memoria es el rendimiento. Esto generalmente viene en cualquiera de los dos (o ambos) de dos formas: o bien la mejora de la velocidad, o la reducción de la fragmentación. Se ha mejorado la velocidad por el hecho de que el administrador de memoria se sólo lidiar con bloques de un tamaño en particular, por lo que puede devolver la dirección de cualquier bloque libre en lugar de gastar tiempo en comprobar si un bloque es lo suficientemente grande, la división de un bloque en dos si es demasiado grande, etc. La fragmentación es reducido (en su mayoría) de la misma manera, por ejemplo, pre-asignación de un bloque lo suficientemente grande como para N objetos da exactamente el espacio necesario para N objetos; la asignación de un objeto de la pena de memoria asignará exactamente el espacio de un objeto, y no de un solo byte más.

    Hay una gran variedad de razones para la sobrecarga de la memoria global de la gestión de los operadores. Muchos de estos están orientados hacia la depuración o la instrumentación, tales como el seguimiento del total de la memoria requerida por una aplicación (por ejemplo, en la preparación para la migración a un sistema embebido), o la depuración de problemas de la memoria, mostrando a los desajustes entre asignar y liberar memoria. Otra estrategia común es asignar memoria adicional antes y después de los límites de cada bloque solicitado, y la escritura los patrones únicos en esas áreas. Al final de la ejecución (y posiblemente en otras ocasiones), esas áreas son examinados para ver si en el código se ha escrito fuera de los límites. Sin embargo, otra es intentar mejorar la facilidad de uso mediante la automatización de al menos algunos aspectos de la asignación de memoria o de eliminación, tales como una automatizado recolector de basura.

    Un no-global predeterminada de asignación de puede ser utilizado para mejorar el rendimiento. Un caso típico sería la sustitución de un defecto asignador de que estaba lento en general (por ejemplo, al menos algunas versiones de MS VC++ alrededor de 4.x llamaría el sistema HeapAlloc y HeapFree funciones para cada asignación y operación de eliminación). Otra posibilidad que he visto en la práctica se produjo en los procesadores Intel cuando se utiliza la ESS operaciones. Estos operan sobre datos de 128 bits. Mientras que las operaciones de trabajo, independientemente de la alineación, se ha mejorado la velocidad cuando los datos se alinea a 128 bits límites. Algunos compiladores (por ejemplo, MS VC++ de nuevo2) no necesariamente forzada de alineación para que las grandes límite, así que a pesar de código utilizando el valor predeterminado de asignador de trabajo sería, en sustitución de la asignación podría proporcionar una sustancial mejora de la velocidad de las operaciones.


    1. La mayoría de los requisitos son cubiertos en el artículo 3.7.3 y el artículo 18.4 de la estándar de C++ (o §3.7.4 y §18.6 en C++0x, al menos a partir de N3291).
    2. Me siento obligado a señalar que no tengo la intención de recoger en Microsoft compilador — dudo de que tiene un número inusual de este tipo de problemas, pero sucede que yo lo uso mucho, por lo que tienden a ser muy consciente de sus problemas.
  3. 6

    Muchas arquitecturas de computadora requieren que los datos de tipos particulares de ser colocado en la memoria en determinados tipos de direcciones. Por ejemplo, una arquitectura podría requerir que los punteros se producen en las direcciones que son un múltiplo de cuatro (es decir, ser de cuatro bytes alineados) o que duplica debe ocurrir en direcciones que son un múltiplo de ocho (es decir, ser de ocho bytes alineados). Incumplimiento de dichas restricciones pueden conducir a hardware excepciones en tiempo de ejecución. Otras arquitecturas son más indulgentes, y puede permitir que el trabajo a pesar de la reducción del rendimiento.

    Para aclarar: si una arquitectura requiere, por ejemplo, que double de los datos de ocho bytes alineado, entonces no hay nada que optimizar. Cualquier tipo de asignación dinámica de tamaño adecuado (por ejemplo,malloc(size), operator new(size), operator new[](size), new char[size] donde size >= sizeof(double)) está garantizado para estar correctamente alineado. Si una aplicación no hace esta garantía, por lo que no es conforme. El cambio de operator new a hacer «lo correcto» en ese caso sería un intento de «fijar» la aplicación, no una optimización.

    Por otro lado, algunas arquitecturas permiten diferentes (o todos) los tipos de alineación para uno o más tipos de datos, pero ofrecen diferentes garantías de rendimiento dependiendo de la alineación de los mismos tipos. Una aplicación puede, a continuación, devolver la memoria (de nuevo, suponiendo una solicitud de tamaño adecuado) que es sub-óptima alineados, y todavía estar de acuerdo. Esto es lo que el ejemplo es sobre.

  4. 4

    Relativa a las estadísticas de uso: elaboración de presupuestos por subsistema. Por ejemplo, en una consola basada en el juego, usted podría desea reservar alguna fracción de la memoria para 3D de la geometría del modelo, algunas de las texturas, algunos de los sonidos, algunos de scripts de juego, etc. Asignadores personalizados pueden etiquetar cada asignación por subsistema y emitir un aviso cuando los presupuestos son excedidos.

  5. 4

    El operador nuevo que se suministran con algunos compiladores no es garantía de ocho bytes de alineación para asignaciones dinámicas de dobles.

    Cita, por favor. Normalmente, el valor predeterminado nuevo operador sólo es ligeramente más complejo que un malloc contenedor, que, por la norma, devuelve la memoria que está debidamente alineado para CUALQUIER tipo de datos que la arquitectura que se apoya.

    No estoy diciendo que no hay buenas razones para la sobrecarga de new y delete para las propias clases… y he tocado en varios legítimo de aquí, pero la de arriba no es uno de ellos.

    • En Windows, algunas de las funciones de la API requieren estrictas alineación que el requerido por el normal C código de acceso a la estructura. Por ejemplo, el búfer para ReadDirectoryChangesW debe ser DWORD-alineados.
    • malloc se supone para devolver la alineación apropiada para TODOS los tipos de datos de la arquitectura de destino. Muy a menudo, va a ser una de 16 bytes alineado de direcciones desde un 16 bytes alineado de dirección también es de 8, 4, 2 y 1 byte alineados. Esto cubre todos los tipos de datos básicos y la estructura de los requisitos de alineación, incluyendo aquellos exigidos por las Ventanas. Ahora, si algunas implementaciones de malloc están volviendo extraño alineaciones, sería agradable ver que están haciendo y por qué.
    • Contexto. Para aquellas arquitecturas donde no alineados acceso está permitido, pero subóptima (y que son mencionadas inmediatamente antes), una aplicación es libre de hacer exactamente lo que está en su cotización — o ver mi respuesta.
    • Sí, al parecer estaba equivocado – me acordé de haber un error en relación a eso, pero no podía replicar – Win32/VS2010 malloc parece regresar de 8 bytes alineado a los punteros.
    • De nuevo… citar las referencias. Usted es simplemente la reformulación de la premisa de que yo estaba disputando sin que ilustra cualquier realidad existente, por ejemplo. Si un compilador del malloc aplicación a devolver no se alinean de manera óptima puntero «porque puede» incluso cuando un programa que supuestamente está compilado para el rendimiento, que sería un gravísimo error. No es que todos los compiladores son libres de errores, pero he utilizado muchas C y los compiladores de C++ a través de los años y no ha encontrado ninguna que no utilice la alineación óptima de la dirección de devoluciones de malloc.
  6. 3

    Yo lo he utilizado para asignar objetos específicos en una memoria compartida de la arena. (Esto es similar a lo que @Russell Borogove citado).

    Años he desarrollado software para la CUEVA. Es un multi-pared sistema de VR. Se utiliza un ordenador a la unidad de cada proyector; 6 fue el máximo (4 paredes, piso y techo), mientras que el 3 fue el más común (2 paredes y el piso). Las máquinas que se comunican en especial de la memoria compartida de hardware.

    Para el apoyo, yo derivados de mi normal (no de la CUEVA) escena de clases el uso de una nueva «nueva» que puso a la información de la escena directamente en la memoria compartida de la arena. Después pasé por ese puntero al esclavo representadores en las diferentes máquinas.

  7. 3

    Parece que vale la pena repetir la lista de mi respuesta de «Cualquier razón a la sobrecarga global new y delete?» aquí — ver que respuesta (o, de hecho,otras respuestas a esa pregunta) para una discusión más detallada, referencias, y otras razones. Estas razones se aplican generalmente a los operadores locales sobrecargas así como default/global, y Cmalloc/calloc/realloc/free sobrecargas o ganchos así.

    Nos sobrecargar el mundial de nuevo y eliminar a los operadores de donde yo trabajo para muchos
    razones:

    • la agrupación de todas las asignaciones pequeñas — disminuye la sobrecarga, disminuye la fragmentación, puede aumentar el rendimiento de las pequeñas-alloc-pesado apps
    • encuadre asignaciones con un conocido de toda la vida — ignore todos los libera hasta el final de este período, luego de liberar a todos ellos
      juntos (la verdad es que hacer esto más con el operador local sobrecargas
      que el global)
    • alineación de ajuste — a cacheline límites, etc
    • alloc llenar — ayudando a exponer el uso de variables no inicializadas
    • relleno gratuito — ayudando a exponer el uso de los previamente eliminados de la memoria
    • retraso libre – el aumento de la eficacia de relleno gratuito, ocasionalmente aumento de rendimiento de
    • centinelas o fenceposts — ayudando a exponer desbordamientos de búfer, diferencias, y el ocasional salvaje puntero
    • redirigir asignaciones — para dar cuenta de NUMA, especiales, áreas de memoria, o incluso para mantener separados los sistemas por separado en la memoria (por ej.
      incrustado lenguajes de secuencias de comandos o Dsl)
    • de recolección de basura o de limpieza, de nuevo útil para aquellos incorporado lenguajes de secuencias de comandos
    • montón de verificación — usted puede caminar a través de la pila de la estructura de datos de cada N asignaciones/libera para asegurarse de que todo se ve bien
    • contabilidad, incluyendo la localización de fugas y el uso de snapshots/estadísticas (pilas, la asignación de edades, etc)

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea