移动语义、奇怪的重复模板模式和内存泄漏

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" 的两个例子:

  1. 表示一个图,其中每个节点都关联到 "MyObj" 的一个实例。我们在每个图节点存储一个索引,并使用全局映射来获取 "MyObj".
  2. 的相应实例
  3. 在范围内创建 "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.

如果你注释掉静态映射,内存泄漏就没有了。