Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用typeinfo::name()后内存泄漏_C++_Visual C++_Memory Leaks_Typeid_Typeinfo - Fatal编程技术网

C++ 使用typeinfo::name()后内存泄漏

C++ 使用typeinfo::name()后内存泄漏,c++,visual-c++,memory-leaks,typeid,typeinfo,C++,Visual C++,Memory Leaks,Typeid,Typeinfo,我有一个程序,其中部分用于信息日志记录,我在使用某些类时输出它们的名称(具体地说,我在日志中添加了一个条目,按照传输到127.0.0.1的消息::CSomeClass的顺序)。我使用与以下类似的代码执行此操作: std::string getMessageName(void) const { return std::string(typeid(*this).name()); } 是的,在有人指出之前,我意识到typeinfo::name的输出是特定于实现的 据 type_info::n

我有一个程序,其中部分用于信息日志记录,我在使用某些类时输出它们的名称(具体地说,我在日志中添加了一个条目,按照传输到127.0.0.1的
消息::CSomeClass的顺序)。我使用与以下类似的代码执行此操作:

std::string getMessageName(void) const {
    return std::string(typeid(*this).name());
}
是的,在有人指出之前,我意识到
typeinfo::name
的输出是特定于实现的

type_info::name
成员函数将
const char*
返回到表示该类型的人类可读名称的以null结尾的字符串。指向的内存是缓存的,不应直接释放

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

虽然这不是一个大问题,但它看起来很混乱,在长时间的调试会话之后,它很容易隐藏真正的内存泄漏

我环顾四周,发现了一些有用的信息(其中一个答案提供了一些有趣的信息),但我想知道这个内存是否应该由系统正常释放,或者我是否可以在调试时“不注意”泄漏


我确实有一个备份计划,那就是自己编写
getMessageName
方法,而不是依赖
typeinfo::name
,但我想知道我是否遗漏了什么。

正如Chris Parton在评论中指出的,这似乎是一个,至少对于我正在使用的编译器版本,如果我能够升级,升级到VC11可以纠正这个问题

尝试删除
typeinfo::name()
的输出部分有效:

std::string getMessageName(void) const
{
    std::string typeStr(typeid(*this).name());
    delete (typeid(*this).name());
    return typeStr;
}
但是仍然存在一些内存泄漏——我只是注意到,以前我似乎每次调用都会出现两次泄漏(可能是因为类位于名称空间内?)。使用上述版本的代码,每次调用都会出现一次泄漏


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

我刚刚偶然发现这个问题,试图清理的日志。是的,这是a,仅在VC11中固定。它存在于MSVC的早期版本中,包括2010年。此错误仅在使用MFC时出现。如果使用MFC作为DLL而不是静态库,则内存泄漏仍然存在,但不会被检测到

有一个名为
的全局缓存,未被清除(摘自
):

我们的想法是清除这个缓存。此功能适用于我:

#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;
   }
}
或者在离开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;
   ...
}

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

由于这只适用于_debugbuilds,以下可能是更安全的解决方案。在退出模块入口点(main、WinMain等)之前,应如上所述调用函数_FixTypeInfoBlockUse()

#如果定义(_DEBUG)&&(_MSC_VER>=1000&&u MSC_VER\u Next;
(((((CrtMemBlockHeader*)pNode)-1->nBlockUse=\U CRT\U块;
如果(pNode->_MemPtr!=NULL)
(((((_CrtMemBlockHeader*)pNode->_MemPtr)-1->nBlockUse=_CRT_BLOCK;
pNode=pNext;
}
}

#endif//defined(_DEBUG)&&(_MSC_VER>=1000&&&u MSC_VERVS将类型信息存储在一个单独链接的列表中。此列表的标题可以通过一个不透明结构访问,该结构可以通过名称\u type_info\u root\u节点访问。实际上它是一个SLIST\u标题结构

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;
}
#包括
#包括
#包括
void ClearTypeinfoCache()
{
#ifdef_调试
while(自动输入=InterlocatedPopEntrySlist(重新解释转换(&&uuuuu类型\uu信息\uu根节点)))
{
免费(入境);
}
#恩迪夫
}
int main()
{
atexit(ClearTypeinfoCache);
返回0;
}

更新:VLD2.5.1未报告类型_info::name()上的内存泄漏在VS2015更新3中。

可能相关?您使用的是什么编译器?如果可能的话,可以尝试其他编译器?因为它是缓存的,所以不要担心它。@jagansai:我不担心泄漏本身,因为它只影响应用程序退出时的调试器输出-我担心它可能隐藏真正的内存泄漏。而且它看起来很混乱。我喜欢整洁的调试器输出:)@ChrisParton:谢谢你的链接-在谷歌搜索时没有发现这一点(我认为我是一个优秀的谷歌搜索者)。我使用的是VC++2008,它有“bug”,目前无法移动到任何更新的版本。我注意到有一个解决方法(手动删除typeinfo实例?),我会很快试用。@icabod:不用担心,让我知道它是怎么回事。在没有其他人的情况下接受我自己的答案。是的,你只删除了一个字符串的内存,因此会出现一次泄漏。还有相应的要删除的单链表节点。请参阅我的答案。这是一个非常糟糕的解决方案,因为字符串缓存在内存中,因此如果你使用c所有typeid(*this).name()在删除之后,您将再次得到一个垃圾字符串。将我接受的答案更改为该字符串,因为它比其他答案更干净(?),部分原因是
#if
将其限制为仅受影响的版本。
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;
   ...
}
#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)
#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;
}