3

当我尝试这个时:

#include <functional>
#include <iostream>
#include <memory>
#include <set>

template<class T>
struct MyAlloc
{
    typedef std::allocator<T> Base;
    typedef typename Base::value_type value_type;
    typedef typename Base::pointer pointer;
    typedef typename Base::const_pointer const_pointer;
    typedef typename Base::reference reference;
    typedef typename Base::const_reference const_reference;
    typedef typename Base::size_type size_type;
    typedef typename Base::difference_type difference_type;
    Base a;
    MyAlloc() { }
    template<class U> MyAlloc(MyAlloc<U> const &) { }
    template<class U> struct rebind { typedef MyAlloc<U> other; };
    pointer allocate(size_type n, void const * = NULL)
    {
        std::cout << "Allocating " << n << " objects" << std::endl;
        return this->a.allocate(n);
    }
    void deallocate(pointer p, size_type n) { return this->a.deallocate(p, n); }
};

int main(int argc, char *argv[])
{
    std::set<int, std::less<int>, MyAlloc<int> > set;
}

我明白了Allocating 1 objects

但我不明白——为什么这个堆分配是必要的?堆栈内存足以默认构建其他容器(如) ,std::vector那么为什么需要堆分配呢?setmap

4

2 回答 2

0

我想我自己想通了。Visual C++ 似乎是正确的,而 Clang 和 GCC 似乎是错误的。

这是因为不应该使or的swap迭代器无效。如果我们尝试这段代码:std::setstd::map

#include <set>
#include <iostream>
int main()
{
    std::set<int> a, b;
    std::set<int>::iterator end = a.end();
    a.swap(b);
    b.insert(end, 1);
    std::cout << b.size() << std::endl;
    return 0;
}

如果树的头部存储在堆栈中,那么end在交换之后将变得无效。

Visual C++ 处理得很好,但 GCC 和 Clang 无限循环(至少在我的版本上)。

编辑:由于模棱两可,上述可能是 C++03 之前的原因,但自 C++11 以来不再是这种情况——请参阅下面的评论。

于 2013-12-29T22:24:18.567 回答
0

C++ 标准当然不要求为默认构造对象分配内存。为什么一个实现可能会选择在std::map<...>我不知道的默认构造函数中分配。一个原因可能是确保它不会尝试将可能很大的分配器嵌入到堆栈分配的对象中。您需要查看实现以了解它为什么在堆上分配内存。

于 2013-12-29T21:48:10.070 回答