移动语义、奇怪的重复模板模式和内存泄漏
Move semantics, Curiously recurring template pattern and memory leak
我已经实现了奇怪的重复模板模式的变化:
#include "stdafx.h"
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <stdlib.h>
template<typename T>
struct Indexed
{
private:
static int s_maxIndex;
static std::map<int, T> s_map;
int m_index;
void SetNewIndex () { m_index = ++s_maxIndex; }
protected:
Indexed (T&& t)
{
SetNewIndex ();
s_map.insert (std::pair<int, T> (m_index, std::move (t)));
}
T& GetMappedRef () { return s_map[m_index]; }
static T& GetMappedRef (int index) { return s_map[index]; }
public:
int GetIndex () const { return m_index; }
static std::map<int, Indexed> const& GetAll () { return s_map; }
static void Clear () { s_map.clear (); }
};
template<typename T>
int Indexed<T>::s_maxIndex = 0;
template<typename T>
std::map<int, T> Indexed<T>::s_map;
struct MyObj
{
int m_myData;
MyObj () = default;
MyObj (int data)
:m_myData (data)
{
}
};
struct MyObjHandler : public Indexed<MyObj>
{
MyObjHandler (int value)
:Indexed (MyObj (value))
{
}
MyObj& GetObjRef ()
{
return GetMappedRef ();
}
static MyObj& GetObjRef (int index)
{
return GetMappedRef (index);
}
};
void test ()
{
MyObjHandler objH1 (4), objH2 (5);
std::cout << objH1.GetIndex () << ", " << objH2.GetIndex () << std::endl;
std::cout << objH1.GetObjRef().m_myData << ", " << objH2.GetObjRef().m_myData << std::endl;
}
int _tmain (int argc, _TCHAR* argv[])
{
test ();
_CrtDumpMemoryLeaks ();
system ("pause");
return 0;
}
以下是使用 "MyObjHandler" 而不是 "MyObj" 的两个例子:
- 表示一个图,其中每个节点都关联到 "MyObj" 的一个实例。我们在每个图节点存储一个索引,并使用全局映射来获取 "MyObj".
的相应实例
在范围内创建 "MyObj" 的实例会使该实例在范围末尾超出范围:
void CreateObj()
{
MyObj 对象;
//对obj的操作;
}
//obj 超出范围
但我想保留该实例以备后用。因此我需要利用移动语义的力量。
它似乎可以工作,但它会导致内存泄漏:
Detected memory leaks!
Dumping objects ->
{216} normal block at 0x0063D1A0, 24 bytes long. Data: < c H c c
A8 D0 63 00 48 D1 63 00 A8 D0 63 00 00 00 CD CD
{215} 正常块位于 0x0063D148,24 字节长。数据:< c c c
A8 D0 63 00 A8 D0 63 00 A0 D1 63 00 01 00 CD CD
{214} 正常块位于 0x0063D100,8 字节长。数据:<@> 40
A3 14 01 00 00 00 00
{213} 正常块位于 0x0063D0A8,24 字节长。数据:
48 D1 63 00 48 D1 63 00 A0 D1 63 00 01 01 CD CD
对象转储完成。
为什么?
我觉得很正常。您可以尝试注释对 test
函数的调用,内存泄漏仍然存在(较小,但仍然存在)。
在我看来,_CrtDumpMemoryLeaks 错误地将静态映射识别为内存泄漏 - 它是在程序开始时分配的,并且仍在使用中。此行为与指定的行为一致 here :
When an unfreed block is encountered, _CrtDumpMemoryLeaks calls
_CrtMemDumpAllObjectsSince to dump information for all the objects allocated in the heap from the start of program execution.
如果你注释掉静态映射,内存泄漏就没有了。
我已经实现了奇怪的重复模板模式的变化:
#include "stdafx.h"
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <stdlib.h>
template<typename T>
struct Indexed
{
private:
static int s_maxIndex;
static std::map<int, T> s_map;
int m_index;
void SetNewIndex () { m_index = ++s_maxIndex; }
protected:
Indexed (T&& t)
{
SetNewIndex ();
s_map.insert (std::pair<int, T> (m_index, std::move (t)));
}
T& GetMappedRef () { return s_map[m_index]; }
static T& GetMappedRef (int index) { return s_map[index]; }
public:
int GetIndex () const { return m_index; }
static std::map<int, Indexed> const& GetAll () { return s_map; }
static void Clear () { s_map.clear (); }
};
template<typename T>
int Indexed<T>::s_maxIndex = 0;
template<typename T>
std::map<int, T> Indexed<T>::s_map;
struct MyObj
{
int m_myData;
MyObj () = default;
MyObj (int data)
:m_myData (data)
{
}
};
struct MyObjHandler : public Indexed<MyObj>
{
MyObjHandler (int value)
:Indexed (MyObj (value))
{
}
MyObj& GetObjRef ()
{
return GetMappedRef ();
}
static MyObj& GetObjRef (int index)
{
return GetMappedRef (index);
}
};
void test ()
{
MyObjHandler objH1 (4), objH2 (5);
std::cout << objH1.GetIndex () << ", " << objH2.GetIndex () << std::endl;
std::cout << objH1.GetObjRef().m_myData << ", " << objH2.GetObjRef().m_myData << std::endl;
}
int _tmain (int argc, _TCHAR* argv[])
{
test ();
_CrtDumpMemoryLeaks ();
system ("pause");
return 0;
}
以下是使用 "MyObjHandler" 而不是 "MyObj" 的两个例子:
- 表示一个图,其中每个节点都关联到 "MyObj" 的一个实例。我们在每个图节点存储一个索引,并使用全局映射来获取 "MyObj". 的相应实例
在范围内创建 "MyObj" 的实例会使该实例在范围末尾超出范围:
void CreateObj() {
MyObj 对象;
//对obj的操作;
} //obj 超出范围
但我想保留该实例以备后用。因此我需要利用移动语义的力量。
它似乎可以工作,但它会导致内存泄漏:
Detected memory leaks!
Dumping objects ->
{216} normal block at 0x0063D1A0, 24 bytes long. Data: < c H c c
A8 D0 63 00 48 D1 63 00 A8 D0 63 00 00 00 CD CD
{215} 正常块位于 0x0063D148,24 字节长。数据:< c c c
A8 D0 63 00 A8 D0 63 00 A0 D1 63 00 01 00 CD CD
{214} 正常块位于 0x0063D100,8 字节长。数据:<@> 40 A3 14 01 00 00 00 00
{213} 正常块位于 0x0063D0A8,24 字节长。数据:
48 D1 63 00 48 D1 63 00 A0 D1 63 00 01 01 CD CD
对象转储完成。
为什么?
我觉得很正常。您可以尝试注释对 test
函数的调用,内存泄漏仍然存在(较小,但仍然存在)。
在我看来,_CrtDumpMemoryLeaks 错误地将静态映射识别为内存泄漏 - 它是在程序开始时分配的,并且仍在使用中。此行为与指定的行为一致 here :
When an unfreed block is encountered, _CrtDumpMemoryLeaks calls _CrtMemDumpAllObjectsSince to dump information for all the objects allocated in the heap from the start of program execution.
如果你注释掉静态映射,内存泄漏就没有了。