Me gustan todas las respuestas sobre cómo la cadena se puede asignar estáticamente, pero eso no es necesariamente cierto para todas las implementaciones, especialmente aquella a la que se vinculó el cartel original. En este caso, parece que el nombre del tipo decorado se almacena estáticamente para ahorrar espacio, y el nombre del tipo no decorado se calcula bajo demanda y se almacena en caché en una lista vinculada.
Si tiene curiosidad acerca de cómo la implementación de Visual C++ type_info::name()
asigna y almacena en caché su memoria, no es difícil de averiguar. En primer lugar, crear un pequeño programa de prueba:
#include <cstdio>
#include <typeinfo>
#include <vector>
int main(int argc, char* argv[]) {
std::vector<int> v;
const type_info& ti = typeid(v);
const char* n = ti.name();
printf("%s\n", n);
return 0;
}
Construir y ejecutarlo con un depurador (utilicé WinDbg) y mirar el puntero devuelto por type_info::name()
. ¿Señala a una estructura global? Si es así, la orden de WinDBG ln
le dirá el nombre del símbolo más cercano:
0:000> ?? n
char * 0x00000000`00857290
"class std::vector<int,class std::allocator<int> >"
0:000> ln 0x00000000`00857290
0:000>
ln
no se imprime nada, lo que indica que la cadena no estaba en el rango de direcciones propiedad de cualquier módulo específico. Estaría en ese rango si estuviera en el segmento de datos o de solo lectura. Vamos a ver si se le asignó en el montón, mediante la búsqueda en todos los montones para la dirección devuelta por type_info::name()
:
0:000> !heap -x 0x00000000`00857290
Entry User Heap Segment Size PrevSize Unused Flags
-------------------------------------------------------------------------------------------------------------
0000000000857280 0000000000857290 0000000000850000 0000000000850000 70 40 3e busy extra fill
Sí, se asignaron en el montón. Poner un punto de interrupción al comienzo de malloc()
y reiniciar el programa lo confirma.
En cuanto a la declaración de <typeinfo>
da una pista acerca de dónde los punteros montón están siendo cacheados:
struct __type_info_node {
void *memPtr;
__type_info_node* next;
};
extern __type_info_node __type_info_root_node;
...
_CRTIMP_PURE const char* __CLR_OR_THIS_CALL name(__type_info_node* __ptype_info_node = &__type_info_root_node) const;
Si encuentra la dirección de __type_info_root_node
y camina por la lista en el depurador, a encontrar rápidamente un nodo que contiene la misma dirección devuelta por type_info::name()
. La lista parece estar relacionada con el esquema de almacenamiento en caché.
La página MSDN vinculada en la pregunta original parece llenar los espacios en blanco: el nombre se almacena en su forma decorada para ahorrar espacio, y este formulario es accesible a través del type_info::raw_name()
. Cuando llama al type_info::name()
por primera vez en un tipo determinado, descifra el nombre, lo almacena en un búfer asignado en el montón, almacena en caché el puntero del búfer y lo devuelve.
La lista vinculada también se puede utilizar para desasignar las cadenas en caché durante la salida del programa (sin embargo, no verifiqué si ese es el caso). Esto garantizaría que no aparezcan como pérdidas de memoria cuando ejecuta una herramienta de depuración de memoria.
Este apareció justo cuando estaba a punto de publicar una solución casi idéntica :) – workmad3
suena razonable , pero ¿no será eso, en la práctica, lo mismo que una nueva llamada? Quiero decir, ¿tendrías una cadena que se asignará durante toda la vida de mi aplicación? ¿Qué sucede si llama a este método toneladas de veces? ¿O qué pasa si el valor de retorno no es conocido en tiempo de compilación? – Meeh
La cadena se ubicará en el almacenamiento estático. No habrá asignación de memoria ni desasignación con esta cadena, y también cualquier intento de eliminar una cadena en almacenamiento estático es un comportamiento indefinido. Llamar al método muchas veces solo devolverá el mismo puntero al almacenamiento estático. – workmad3