c++ memory-management language-lawyer compiler-optimization

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.