c++ - keywords - meta tags ejemplos
¿Se puede utilizar la ubicación nueva para las matrices de una manera portátil? (7)
@Derek
5.3.4, la sección 12 habla sobre la sobrecarga de asignación de matrices y, a menos que esté malinterpretando, parece sugerirme que es válido para el compilador agregarlo también en la ubicación nueva:
Esta sobrecarga se puede aplicar en todas las expresiones nuevas de matriz, incluidas las que hacen referencia al operador de función de biblioteca new [] (std :: size_t, void *) y otras funciones de asignación de ubicación. La cantidad de sobrecarga puede variar de una invocación de nueva a otra.
Dicho esto, creo que VC fue el único compilador que me dio problemas con esto, fuera de él, GCC, Codewarrior y ProDG. Sin embargo, tendría que volver a verificar para estar seguro.
¿Es posible usar la ubicación nueva en el código portátil cuando se usa para arreglos?
Parece que el puntero que obtiene de nuevo [] no es siempre el mismo que la dirección que ingresa (5.3.4, la nota 12 en el estándar parece confirmar que esto es correcto), pero no veo cómo puede asignar un búfer para la matriz para entrar si este es el caso.
El siguiente ejemplo muestra el problema. Compilado con Visual Studio, este ejemplo produce daños en la memoria:
#include <new>
#include <stdio.h>
class A
{
public:
A() : data(0) {}
virtual ~A() {}
int data;
};
int main()
{
const int NUMELEMENTS=20;
char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
A *pA = new(pBuffer) A[NUMELEMENTS];
// With VC++, pA will be four bytes higher than pBuffer
printf("Buffer address: %x, Array address: %x/n", pBuffer, pA);
// Debug runtime will assert here due to heap corruption
delete[] pBuffer;
return 0;
}
Al observar la memoria, el compilador parece estar utilizando los primeros cuatro bytes del búfer para almacenar un recuento de la cantidad de elementos en él. Esto significa que debido a que el buffer es solo sizeof(A)*NUMELEMENTS
big, el último elemento de la matriz se escribe en un montón no asignado.
Entonces, la pregunta es: ¿puede averiguar cuánto sobrecarga adicional quiere su implementación para usar la ubicación nueva [] de forma segura? Idealmente, necesito una técnica que sea portátil entre diferentes compiladores. Tenga en cuenta que, al menos en el caso de VC, la sobrecarga parece diferir para diferentes clases. Por ejemplo, si elimino el destructor virtual en el ejemplo, la dirección devuelta desde new [] es la misma que la dirección que paso.
@James
Ni siquiera estoy muy claro por qué necesita los datos adicionales, ya que de todos modos no llamaría eliminar [] en la matriz, por lo que no veo por qué necesita saber cuántos elementos contiene.
Después de pensarlo un poco, estoy de acuerdo contigo. No hay ninguna razón por la cual la ubicación nueva deba almacenar la cantidad de elementos, porque no hay eliminación de ubicación. Como no hay eliminación de ubicación, no hay ninguna razón para la colocación nueva para almacenar la cantidad de elementos.
También probé esto con gcc en mi Mac, usando una clase con un destructor. En mi sistema, la colocación nueva no cambiaba el puntero. Esto me hace preguntarme si esto es un problema de VC ++, y si esto podría violar el estándar (el estándar no aborda específicamente esto, hasta donde puedo encontrar).
Creo que gcc hace lo mismo que MSVC, pero por supuesto esto no lo hace "portátil".
Creo que puedes evitar el problema cuando NUMELEMENTS es realmente una constante de tiempo de compilación, así:
typedef A Arr[NUMELEMENTS];
A * p = new (buffer) Arr;
Esto debería usar la colocación escalar nueva.
De forma similar a cómo usaría un elemento único para calcular el tamaño de una ubicación nueva, use una matriz de esos elementos para calcular el tamaño requerido para una matriz.
Si necesita el tamaño para otros cálculos donde no se conoce la cantidad de elementos, puede usar sizeof (A [1]) y multiplicar por el recuento de elementos requerido.
p.ej
char *pBuffer = new char[ sizeof(A[NUMELEMENTS]) ];
A *pA = (A*)pBuffer;
for(int i = 0; i < NUMELEMENTS; ++i)
{
pA[i] = new (pA + i) A();
}
Gracias por las respuestas Usar la ubicación nueva para cada elemento en la matriz fue la solución que terminé usando cuando me topé con esto (lo siento, debería haberlo mencionado en la pregunta). Simplemente sentí que debía haber algo que me faltaba al hacerlo con la colocación nueva []. Tal como está, parece que la colocación nueva [] es esencialmente inutilizable gracias al estándar que permite al compilador añadir una sobrecarga adicional no especificada a la matriz. No veo cómo podrías usarlo de manera segura y portátil.
Ni siquiera estoy muy claro por qué necesita los datos adicionales, ya que de todos modos no llamaría eliminar [] en la matriz, por lo que no veo por qué necesita saber cuántos elementos contiene.
La ubicación nueva en sí misma es portátil, pero las suposiciones que hace sobre lo que hace con un bloque de memoria específico no son portátiles. Al igual que lo que se dijo antes, si fuera un compilador y se le diera un trozo de memoria, ¿cómo sabría cómo asignar una matriz y destruir adecuadamente cada elemento si todo lo que tuviera fuera un puntero? (Consulte la interfaz del operador delete []).
Editar:
Y en realidad hay una eliminación de ubicación, solo se llama cuando un constructor lanza una excepción al asignar una matriz con ubicación nueva [].
Si algo nuevo [] en realidad necesita para realizar un seguimiento de la cantidad de elementos de alguna manera es algo que queda a la altura del estándar, que lo deja en manos del compilador. Lamentablemente, en este caso.
Personalmente, me gustaría ir con la opción de no utilizar la ubicación nueva en la matriz y, en su lugar, utilizar la colocación nueva en cada elemento de la matriz de forma individual. Por ejemplo:
int main(int argc, char* argv[])
{
const int NUMELEMENTS=20;
char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
A *pA = (A*)pBuffer;
for(int i = 0; i < NUMELEMENTS; ++i)
{
pA[i] = new (pA + i) A();
}
printf("Buffer address: %x, Array address: %x/n", pBuffer, pA);
// dont forget to destroy!
for(int i = 0; i < NUMELEMENTS; ++i)
{
pA[i].~A();
}
delete[] pBuffer;
return 0;
}
Independientemente del método que utilice, asegúrese de destruir manualmente cada uno de esos elementos en la matriz antes de eliminar pBuffer, ya que podría terminar con filtraciones;)
Nota : No he compilado esto, pero creo que debería funcionar (estoy en una máquina que no tiene un compilador C ++ instalado). Todavía indica el punto :) Espero que ayude de alguna manera!
Editar:
La razón por la que necesita realizar un seguimiento del número de elementos es para que pueda recorrerlos cuando llame a delete en la matriz y asegurarse de que se invocan los destructores en cada uno de los objetos. Si no sabe cuántos hay, no podría hacer esto.