我喜欢所有关于如何静态分配字符串的答案,但这并不一定适用于所有实现,尤其是原始海报链接到其文档的实现。在这种情况下,装饰类型名称似乎是静态存储的以节省空间,而未装饰类型名称是按需计算并缓存在链表中的。
如果您对 Visual C++ 实现如何分配和缓存其内存感到好奇type_info::name()
,不难发现。首先,创建一个小测试程序:
#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;
}
构建它并在调试器(我使用 WinDbg)下运行它并查看type_info::name()
. 它是否指向一个全球结构?如果是这样,WinDbg 的ln
命令将告诉最接近符号的名称:
0:000> ?? n
char * 0x00000000`00857290
"class std::vector<int,class std::allocator<int> >"
0:000> ln 0x00000000`00857290
0:000>
ln
没有打印任何内容,这表明该字符串不在任何特定模块拥有的地址范围内。如果它在数据或只读数据段中,它将在该范围内。让我们通过在所有堆中搜索以下返回的地址来查看它是否在堆上分配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
是的,它是在堆上分配的。在程序的开头放置一个断点malloc()
并重新启动程序来确认它。
查看中的声明<typeinfo>
可以了解堆指针的缓存位置:
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;
如果您__type_info_root_node
在调试器中找到 的地址并沿着列表向下走,您会很快找到一个包含与type_info::name()
. 该列表似乎与缓存方案有关。
原始问题中链接的 MSDN 页面似乎填补了空白:名称存储在其装饰形式中以节省空间,并且可以通过type_info::raw_name()
. 当您type_info::name()
第一次调用给定类型时,它会取消修饰名称,将其存储在堆分配的缓冲区中,缓存缓冲区指针并返回它。
链表也可用于在程序退出期间释放缓存的字符串(但是,我没有验证是否是这种情况)。这将确保在您运行内存调试工具时它们不会显示为内存泄漏。