c++ - ¿Puede el compilador optimizar desde la asignación de montón a pila?
memory-management language-lawyer (3)
En cuanto a las optimizaciones del compilador, ¿es legal y / o posible cambiar una asignación de montón a una asignación de pila? ¿O eso rompería la regla como si ?
Por ejemplo, digamos que esta es la versión original del código
{
Foo* f = new Foo();
f->do_something();
delete f;
}
¿Podría un compilador cambiar esto a lo siguiente
{
Foo f{};
f.do_something();
}
No lo creo, porque eso tendría implicaciones si la versión original dependiera de cosas como asignadores personalizados. ¿La norma dice algo específicamente sobre esto?
Estos no son equivalentes.
f.do_something()
podría lanzar, en cuyo caso el primer objeto permanece en la memoria, el segundo se destruye.
Me gustaría señalar algo IMO no lo suficientemente estresado en las otras respuestas:
struct Foo {
static void * operator new(std::size_t count) {
std::cout << "Hey ho!" << std::endl;
return ::operator new(count);
}
};
Una asignación
new Foo()
generalmente no se puede reemplazar, porque:
Una implementación puede omitir una llamada a una función de asignación global reemplazable (18.6.1.1, 18.6.1.2). Cuando lo hace, el almacenamiento es proporcionado por la implementación o por la extensión de la asignación de otra nueva expresión.
Por lo tanto, como en el ejemplo de
Foo
anterior, se debe llamar al
Foo::operator new
.
Omitir esta llamada cambiaría el comportamiento observable del programa.
Ejemplo del mundo real:
Foo
s podría necesitar residir en alguna región de memoria especial (como IO mapeada en memoria) para funcionar correctamente.
Si, es legal.
expr.new/10
de C ++ 14:
Una implementación puede omitir una llamada a una función de asignación global reemplazable (18.6.1.1, 18.6.1.2). Cuando lo hace, el almacenamiento es proporcionado por la implementación o por la extensión de la asignación de otra nueva expresión.
expr.delete/7
:
Si el valor del operando de la expresión de eliminación no es un valor de puntero nulo, entonces:
- Si no se omitió la llamada de asignación para la nueva expresión para el objeto a eliminar y la asignación no se extendió (5.3.4), la expresión de eliminación llamará a una función de desasignación (3.7.4.2). El valor devuelto por la llamada de asignación de la nueva expresión se pasará como primer argumento a la función de desasignación.
- De lo contrario, si la asignación se extendió o se proporcionó extendiendo la asignación de otra nueva expresión, y la expresión de eliminación para cada otro valor de puntero producido por una nueva expresión que tenía almacenamiento proporcionado por la nueva expresión extendida se ha evaluado , la expresión de eliminación llamará a una función de desasignación. El valor devuelto por la llamada de asignación de la nueva expresión extendida se pasará como primer argumento a la función de desasignación.
- De lo contrario, la expresión de eliminación no llamará a una función de desasignación (3.7.4.2).
Entonces, en resumen, es legal reemplazar
new
y
delete
con algo de implementación definida, como usar la pila en lugar del montón.
Nota: Como comenta Massimiliano Janes, el compilador no podría
do_something
exactamente a esta transformación para su muestra, si arroja algo: el compilador debe omitir la llamada al destructor de
f
en este caso (mientras que su muestra transformada llama al destructor en este caso).
Pero aparte de eso, es gratis poner
f
en la pila.