我有一个程序,部分是为了信息记录,我输出了某些类的名称(特别是我在日志中添加了一个条目, Messages::CSomeClass transmitted to 127.0.0.1)。我以类似于以下的代码执行此操作:

std::string getMessageName(void) const {
    return std::string(typeid(*this).name());
}

是的,在任何人指出之前,我意识到 typeinfo::name 是特定于实施的。

根据 MSDN

type_info::name 成员功能返回 const char* 到代表该类型的人类可读名称的无效终止字符串。指向的记忆是缓存的,绝对不要直接划分。

但是,当我在调试器中退出程序时,任何“新”的使用 typeinfo::name() 显示为记忆泄漏。如果我输出两个类别的信息,我会得到2个内存泄漏,依此类推。这暗示了缓存的数据永远不会被释放。

尽管这不是一个主要问题,但看起来很混乱,经过长时间的调试会话,它很容易隐藏真正的内存泄漏。

我环顾四周,发现了一些有用的信息(一个答案提供了一些有关的有趣信息 如何实现TypeInfo),但是我想知道该内存是否通常应该被系统释放,或者我可以做一些事情来“不注意”调试时泄漏。

我确实有一个备份计划,那就是编码 getMessageName 我自己而不是依靠 typeinfo::name, ,但是无论如何我想知道是否有我错过的东西。

有帮助吗?

解决方案

另一个解决方案是纠正基本问题。这并不是真正的内存泄漏,只是一个错误的报告。分配给tyepinfo()和名称()字符串的内存块分配了错误的块类型。 “释放”此记忆可能不是一个好主意,因为CRT将尝试再次释放它。好消息是,这最终在VS2012(_MSC_VER 1700+)中修复。

由于这仅适用于_debug构建,因此以下可能是更安全的解决方案。在退出模块入口点(Main,Winmain等)之前,应如上所述调用函数_fixtypeinfoblockuse()。

#if defined(_DEBUG) && (_MSC_VER >= 1000 && _MSC_VER <= 1699)
//
// Debug memory block header:
//    o  Borrowed from the Microsoft CRT to fix the false "memory leak" report
//       when using typeinfo 'name' accessor in a _DEBUG build of the library.  
//
struct _CrtMemBlockHeader
   {
   struct _CrtMemBlockHeader * pBlockHeaderNext;
   struct _CrtMemBlockHeader * pBlockHeaderPrev;
   char *                      szFileName;
   int                         nLine;
   #ifdef _WIN64
   int                         nBlockUse;
   size_t                      nDataSize;
   #else
   size_t                      nDataSize;
   int                         nBlockUse;
   #endif
   long                        lRequest;
   unsigned char               gap[4];
   };

static void __cdecl _FixTypeInfoBlockUse(void)
   {
   __type_info_node* pNode = __type_info_root_node._Next;

   while(pNode != NULL)
      {
      __type_info_node* pNext = pNode->_Next;

      (((_CrtMemBlockHeader*)pNode) - 1)->nBlockUse = _CRT_BLOCK;

      if (pNode->_MemPtr != NULL)
         (((_CrtMemBlockHeader*)pNode->_MemPtr) - 1)->nBlockUse = _CRT_BLOCK;

      pNode = pNext;
      }
   }

#endif//defined(_DEBUG) && (_MSC_VER >= 1000 && _MSC_VER <= 1699)

其他提示

我只是偶然发现了这个问题,试图清洁日志 vld. 。是的,这是一个 已知错误, ,仅在VC11中固定。它存在于MSVC的先前版本中,包括2010年。仅当您使用MFC时,才会出现此错误。如果将MFC用作DLL而不是静态库,则将仍然存在内存泄漏,但不会检测到。

有一个全局缓存的 type_info 名称且未清除(摘录 <typeinfo>):

struct __type_info_node {
    void *_MemPtr;
    __type_info_node* _Next;
};

extern __type_info_node __type_info_root_node;

这个想法是清除此缓存。此功能对我有用:

#include <typeinfo>

void clear_type_info_cache()
{
   __type_info_node* & node = __type_info_root_node._Next;
   while(node)
   {
      if (node->_MemPtr)
      {
         delete node->_MemPtr;
      }
      __type_info_node* tempNode = node;
      node = node->_Next;
      delete tempNode;
   }
}

称呼 clear_type_info_cache() 退出之前。您可以注册 atexit

#include <cstdlib>

int WinMain(...)
{
   atexit(&clear_type_info_cache);
   ...
}

或在离开Winmain之前立即打电话给它

struct dummy_scope_exit
{
   typedef void (*Fun)();
   dummy_scope_exit(Fun f) : m_f(f) {}
   ~dummy_scope_exit() { m_f(); }
   Fun m_f;
};

int WinMain(...)
{
   dummy_scope_exit cleaner = &clear_type_info_cache;
   ...
}

正如克里斯·帕顿(Chris Parton)在评论中指出的那样,这似乎是 已知错误, ,至少使用我正在使用的编译器版本 - 如果我能够升级,将升级到VC11将纠正问题。

试图删除输出 typeinfo::name() 部分工作:

std::string getMessageName(void) const
{
    std::string typeStr(typeid(*this).name());
    delete (typeid(*this).name());
    return typeStr;
}

但是,仍然有一些内存泄漏 - 我只是注意到以前我似乎每个通话都会泄漏两个泄漏(也许是由于类位置在命名空间内吗?)。使用以上版本的代码,每个呼叫的一个泄漏。

似乎有效的另一个解决方案是在MFC库的动态版本中链接(是的,我正在使用MFC,不要判断我),而不是静态版本。

VS存储在单连锁列表中键入信息。可以通过名称访问的不透明结构访问此列表的标题 __TYPE_INFO_ROOT_NODE. 。实际上,这是一个slist_header结构。

Win32 API具有一组并发安全功能,可以与此类结构一起使用。要修复内存泄漏报告,您需要删除此列表的所有节点。

#include <Windows.h>
#include <typeinfo>
#include <vld.h>

void ClearTypeinfoCache()
{
#ifdef _DEBUG
    while (auto entry = InterlockedPopEntrySList(reinterpret_cast<PSLIST_HEADER>(&__type_info_root_node)))
    {
        free(entry);
    }
#endif
}

int main()
{
    atexit(ClearTypeinfoCache);
    return 0;
}

更新:VLD 2.5.1在VS2015中的type_info :: name()上不报告内存泄漏3。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top