类似于矢量的容器,可以使存储相互连续的实例?

时间:2014-05-30 19:55:42

标签: c++ c++11 memory-management vector stl

我需要一个API尽可能接近std :: vector的容器类(除了没有重新分配),但是其元素是'可以指定存储(而不是其成员变量,如大小)从现有缓冲区分配,以便我可以拥有所有向量'将元素保存在连续的缓冲区中。也就是说,一个向量的.end()指向缓冲区中与下一个向量相同的元素。

我不知道我是否可以简单地使用带有std :: vector的自定义分配器,因为我无法找到有关是否为整个类分配存储的信息,包括大小和指针数据成员(在这种情况下,我不能使用这种方法),或只是它拥有的数据元素(在这种情况下我可以使用它)。

我只需要分配一次实例存储,因此重新分配没有问题。我在这里发帖,看看是否已经发布了这样的容器,而不是从头开始重新实现大多数带有迭代器的std向量接口。


更新:我取消选中已发布的答案,因为它在Visual C ++ 2012中无法在调试模式下运行。T = float的示例:

template<class T>
inline typename ContigAlloc<T>::pointer ContigAlloc<T>::allocate(std::size_t n)
{
    std::cout << "Alloc " << n << "; type match: " << std::boolalpha << std::is_same<T, float>::value << std::endl;
    return reinterpret_cast<T *>(_buff.alloc(T * sizeof(n)));
}

template<class T>
inline void ContigAlloc<T>::deallocate(T *p, std::size_t n) // TODO: noexcept when VC++2013
{
    std::cout << "Deall " << n << "; type match: " << std::boolalpha << std::is_same<T, float>::value << std::endl;
    _buff.dealloc(p, T * sizeof(n));
}

测试:

std::vector<float, ContigAlloc<float>> vec;
vec.push_back(1.1f);
vec.push_back(1.9f);

发布版本的结果很好:

Alloc 1; type match: true
Alloc 2; type match: true
Deall 1; type match: true
Deall 2; type match: true

调试版本的结果正常:

Alloc 1; type match: false
Alloc 1; type match: true
Alloc 2; type match: true
Deall 1; type match: true
Deall 2; type match: true
Deall 1; type match: false

在第一次致电allocate()时,T = _Container_proxy

3 个答案:

答案 0 :(得分:7)

分配器仅用于为元素分配存储空间。您可以为此目的使用自定义分配器。

我在下面的评论中由Jon纠正。

认为可以实现符合vector,以便它将所有内容存储在堆上,除了指针。堆上的东西可以是3个指针,加上分配器(如果没有分配器没有被优化掉),或1个指针,大小和容量(以及可能优化的远离分配器)。

在实践中,std::vector的每一个实现都以任何形式发布,包括:

  • HP
  • SGI
  • libstdc ++(gcc)
  • libc ++(llvm)
  • Dinkumware的
  • 微软
  • Rogue Wave
  • 的CodeWarrior
  • STLPort的
  • 我确定我忘了其他人......

将所有支持成员放在vector类本身中,并仅使用allocator来分配数据。除此之外似乎没什么动力。

所以这是事实上的标准,而不是官方标准。根据上述历史,这是一个非常安全的。

请注意,我们无法对string提出相同的声明,string在概念上具有相同的布局。 vector的C ++ 11实现通常会使用&#34;短字符串&#34;优化,其中分配器根本不用于&#34;短&#34;字符串,而是值嵌入在字符串类中。这种优化有效地禁止{{1}}通过23.2.1一般容器要求[container.requirements.general] / 10:

  

(除非另有说明)否则swap()函数无效   引用,指针或迭代器引用的元素   容器被交换。

答案 1 :(得分:2)

如果我正确理解您的问题,您使用的是固定大小的矢量。 如果这些大小和向量的数量是编译时常数,我建议使用std::array

编辑: 只是为了澄清我的意思,这里有一个例子:

struct Memory {
    std::array<int, 2> a1; 
    std::array<int, 2> a2;
} memory; 


int main() {         
    std::array<int, 2>& a1 = memory.a1;
    std::array<int, 2>& a2 = memory.a2; 

    a1[0] = 10; 
    a1[1] = 11;  
    a2[0] = 20;
    a2[1] = 21;  

    int *it=&(a1[0]); 

    for (size_t i = 0; i < 4; ++i){
        std::cout << *(it++) << ",";
    }
}

输出:10,11,20,21, 根据您的要求,您还可以将Memory实现为单身人士。 当然,这只是我的猜测,这是否符合您当前的使用模式。

答案 2 :(得分:0)

好吧,我让它在gcc和Visual C ++ 2012中工作,所以我发帖以防其他人遇到这个问题。我必须在我的allocator类中添加以下内容:

template<class U>
struct rebind
{
    typedef typename std::conditional<std::is_same<T, U>::value, ContigAlloc<U>, std::allocator<U>>::type other;
}

template<class U>
inline operator std::allocator<U>(void) const
{
    return std::allocator<U>();
}

对于Visual C ++ 2012,在Debug构建中似乎需要条件typedef和转换运算符。

这只适用于默认的std :: allocator是无状态的,我不认为是在标准中指定的。