C++ STL中的额外分配和神奇的空间缩减-使用右值引用

C++ STL中的额外分配和神奇的空间缩减-使用右值引用,c++,visual-c++,c++11,C++,Visual C++,C++11,我用一个分配器替换了标准分配器,该分配器将“呼叫总部”,告知它消耗了多少内存。现在我正在浏览我的一些代码,想知道为什么它会分配然后取消分配这么多条目 仅供参考,我并没有试图预先优化我的代码或任何东西,我主要是好奇,除了我肯定需要知道我的总大小是否变小,因为我需要确切地知道我的对象在C#GC中使用了多少 以以下示例函数为例: void add_file(string filename, string source) { file_source_map.insert(std::pair<

我用一个分配器替换了标准分配器,该分配器将“呼叫总部”,告知它消耗了多少内存。现在我正在浏览我的一些代码,想知道为什么它会分配然后取消分配这么多条目

仅供参考,我并没有试图预先优化我的代码或任何东西,我主要是好奇,除了我肯定需要知道我的总大小是否变小,因为我需要确切地知道我的对象在C#GC中使用了多少

以以下示例函数为例:

void add_file(string filename, string source) {
    file_source_map.insert(std::pair<const string, string>(std::move(filename), std::move(source)));
}
void add_文件(字符串文件名,字符串源){
插入(std::pair(std::move(文件名),std::move(源));
}
它分配六次(48字节),然后取消分配四次(32字节)。由于该对是一个右值,并且我将字符串移动到其中,因此映射肯定会分配一个新节点并将右值对移动到其中,而不会触发任何更多的分配,当然也不必取消分配。文件名和源参数也来自右值,应该移入,而不是复制。请注意:分配器也在跟踪字符串,它不是std::string,而是
std::basic_string

仅供参考,我在MSVC上

这是我的分配器代码:

template<typename T>
class Allocator {
public : 
    //    typedefs

    typedef T value_type;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;

public : 
    //    convert an allocator<T> to allocator<U>

    template<typename U>
    struct rebind {
        typedef Allocator<U> other;
    };

public : 
    Parser* parser;
    inline ~Allocator() {}
    inline Allocator(Allocator const& other) {
        parser = other.parser;
    }
    inline Allocator(Parser* ptr)
        : parser(ptr) {}
    template<typename U>
    inline Allocator(Allocator<U> const& other) {
        parser = other.parser;
    }

    //    address

    inline pointer address(reference r) { return &r; }
    inline const_pointer address(const_reference r) { return &r; }

    //    memory allocation

    inline pointer allocate(size_type cnt, 
        typename std::allocator<void>::const_pointer = 0) { 
            int newsize = cnt * sizeof (T);
            parser->size += newsize;
            std::cout << "Allocated " << newsize << "\n";
            return reinterpret_cast<pointer>(::operator new(newsize)); 
    }
    inline void deallocate(pointer p, size_type count) {
        size_type size = count * sizeof(T);
        ::operator delete(p); 
        parser->size -= size;
        std::cout << "Deallocated " << size << "\n";
    }

    //    size

    inline size_type max_size() const { 
        return std::numeric_limits<size_type>::max() / sizeof(T);
    }

    //    construction/destruction

    inline void construct(pointer p, const T& t) { new(p) T(t); }
    inline void destroy(pointer p) { p->~T(); }

    inline bool operator==(Allocator const& other) { return other.parser == parser; }
    inline bool operator!=(Allocator const& a) { return !operator==(a); }
};
模板
类分配器{
公众:
//typedefs
类型定义T值_类型;
typedef值\u type*指针;
类型定义常量值\u类型*常量指针;
类型定义值\u类型和参考;
类型定义常量值\u类型和常量参考;
typedef std::size\u t size\u type;
typedef std::ptrdiff_t difference_type;
公众:
//将分配器转换为分配器
样板
结构重新绑定{
类型定义分配器其他;
};
公众:
解析器*解析器;
内联~Allocator(){}
内联分配器(分配器常量和其他){
parser=other.parser;
}
内联分配器(解析器*ptr)
:解析器(ptr){}
样板
内联分配器(分配器常量和其他){
parser=other.parser;
}
//地址
内联指针地址(引用r){return&r;}
内联常量指针地址(常量引用r){return&r;}
//内存分配
内联指针分配(大小\类型cnt,
typename std::allocator::const_pointer=0){
int newsize=cnt*sizeof(T);
解析器->大小+=新闻大小;

std::cout我在VS 2010中运行了您的代码,我相信您看到的分配只是Visual Studio STL调试工具,因为所有8字节的分配都是从
\u String\u val
构造函数发出的:

  • 在发行版(
    \u迭代器\u调试\u级别==0
    )中,构造函数非常简单
  • 在debug(
    \u ITERATOR\u debug\u LEVEL!=0
    )中,它通过分配器分配一个
    \u Container\u proxy
    (恰好大小为8)

如果我在发布模式下运行代码,映射的节点分配将下降到72字节,8字节分配和释放将消失:字符串似乎已正确移动。

您如何“知道”它将节点调整为16字节?是因为您的分配器吗?问题可能在于分配器逻辑,而不是“魔法”STL代码。@Peter:分配器逻辑非常简单。您分配countsizeof(T),您取消分配countsizeof(T)。如果我错误地跟踪分配,那么我会崩溃,因为STL代码期望的内存比它得到的要多。如果我错误地跟踪释放,那么我会看到内存泄漏。@DeadMG:我建议从您的分配器发布一些相关代码,以及显示16字节/节点的代码。@Peter:原来是我提供了分配器作为谓词,而不是分配器,分配器通过映射解释丢失的节点内存,但不是额外的分配/解除分配。@DeadMG:我刚刚编译了您的代码,
insert
行只在我这边生成一个80的分配(VS 2010):/显然,我的多项目设置管理不当,您完全正确。请看,我曾想过在发布模式下尝试,但当我选中时,它没有删除任何设置。也许我只是没有正确地进行更新,因为这次我注意到,在使用“生成”时,文件上的时间没有更改,但在重建时,它会更改奇怪。然而,这是正确的答案。