¿La ubicación de la matriz nueva requiere una sobrecarga no especificada en el búfer?
5.3.4 [expr.new]
del borrador de C++ del 11 de febrero da el ejemplo:
new(2,f) T[5]
resulta en una llamada deoperator new[](sizeof(T)*5+y,2,f)
.Aquí, xey son valores no negativos no especificados que representan la sobrecarga de asignación de matriz; el resultado de la nueva expresión se compensará con esta cantidad del valor devuelto por
operator new[]
. Esta sobrecarga se puede aplicar en todas las nuevas expresiones de matriz , incluidas aquellas que hacen referencia a la función de bibliotecaoperator new[](std::size_t, void*)
y otras funciones de asignación de ubicación. La cantidad de gastos generales puede variar de una invocación de nuevo a otra. —fin del ejemplo ]
Ahora tome el siguiente código de ejemplo:
void* buffer = malloc(sizeof(std::string) * 10);
std::string* p = ::new (buffer) std::string[10];
Según la cita anterior, la segunda línea new (buffer) std::string[10]
llamará internamente operator new[](sizeof(std::string) * 10 + y, buffer)
(antes de construir los objetos individuales std::string
). ¡El problema es que si y > 0
, el búfer preasignado será demasiado pequeño!
Entonces, ¿cómo sé cuánta memoria debo preasignar cuando utilizo la ubicación de matriz nueva?
void* buffer = malloc(sizeof(std::string) * 10 + how_much_additional_space);
std::string* p = ::new (buffer) std::string[10];
¿O el estándar en algún lugar garantiza eso y == 0
en este caso? Nuevamente la cita dice:
Esta sobrecarga se puede aplicar en todas las nuevas expresiones de matriz , incluidas aquellas que hacen referencia a la función de biblioteca
operator new[](std::size_t, void*)
y otras funciones de asignación de ubicación.
Actualizar
Nicol Bolas señala correctamente en los comentarios a continuación que esto se ha solucionado de modo que los gastos generales sean siempre cero paraoperator new[](std::size_t, void* p)
.
Esta solución se realizó como informe de defectos en noviembre de 2019, lo que la hace retroactiva para todas las versiones de C++.
Respuesta original
No lo use operator new[](std::size_t, void* p)
a menos que sepa a priori la respuesta a esta pregunta. La respuesta es un detalle de implementación y puede cambiar con el compilador/plataforma. Aunque suele ser estable para cualquier plataforma determinada. Por ejemplo, esto es algo especificado por Itanium ABI .
Si no sabe la respuesta a esta pregunta, escriba su propia matriz de ubicación nueva que pueda verificar esto en tiempo de ejecución:
inline
void*
operator new[](std::size_t n, void* p, std::size_t limit)
{
if (n <= limit)
std::cout << "life is good\n";
else
throw std::bad_alloc();
return p;
}
int main()
{
alignas(std::string) char buffer[100];
std::string* p = new(buffer, sizeof(buffer)) std::string[3];
}
Al variar el tamaño de la matriz e inspeccionar n
en el ejemplo anterior, puede inferir y
para su plataforma. Para mi plataforma y
es 1 palabra. El tamaño de (palabra) varía dependiendo de si estoy compilando para una arquitectura de 32 o 64 bits.
Actualización: después de una discusión, entiendo que mi respuesta ya no se aplica a la pregunta. Lo dejaré aquí, pero definitivamente todavía se necesita una respuesta real.
Estaré encantado de apoyar esta pregunta con alguna recompensa si no se encuentra pronto una buena respuesta.
Replantearé la pregunta aquí según tengo entendido, con la esperanza de que una versión más breve pueda ayudar a otros a comprender lo que se pregunta. La pregunta es:
¿La siguiente construcción es siempre correcta? ¿Está arr == addr
al final?
void * addr = std::malloc(N * sizeof(T));
T * arr = ::new (addr) T[N]; // #1
Sabemos por el estándar que el número 1 provoca la llamada ::operator new[](???, addr)
, donde ???
es un número no especificado no menor que N * sizeof(T)
, y también sabemos que esa llamada solo regresa addr
y no tiene otros efectos. También sabemos que arr
se compensa addr
correspondientemente. Lo que no sabemos es si la memoria a la que apunta addr
es lo suficientemente grande o cómo sabríamos cuánta memoria asignar.
Pareces confundir algunas cosas:
Tu ejemplo llama
operator new[]()
, no.operator new()
Las funciones de asignación no construyen nada. Ellos asignan .
Lo que pasa es que la expresión T * p = new T[10];
provoca:
una llamada a
operator new[]()
con argumento de tamaño10 * sizeof(T) + x
,diez llamadas al constructor predeterminado de
T
, efectivamente::new (p + i) T()
.
La única peculiaridad es que la expresión array-new solicita más memoria de la que utilizan los propios datos de la matriz. Usted no ve nada de esto y no puede hacer uso de esta información de ninguna otra manera que no sea mediante la aceptación silenciosa.
Si tiene curiosidad sobre cuánta memoria se asignó realmente, simplemente puede reemplazar las funciones de asignación de matriz operator new[]
y operator delete[]
hacer que se imprima el tamaño real.
Actualización: como información aleatoria, debe tener en cuenta que las nuevas funciones de ubicación global deben ser no operativas. Es decir, cuando construyes un objeto o matriz en el lugar de esta manera:
T * p = ::new (buf1) T;
T * arr = ::new (buf10) T[10];
Luego las llamadas correspondientes a ::operator new(std::size_t, void*)
y ::operator new[](std::size_t, void*)
no hacen más que devolver su segundo argumento. Sin embargo, no sabes a qué buf10
se supone que debe apuntar: necesita apuntar a 10 * sizeof(T) + y
bytes de memoria, pero no puedes saberlo y
.