【问题标题】:std container for C-style arrayC 样式数组的标准容器
【发布时间】:2018-03-23 13:37:34
【问题描述】:

是否有用于可变大小的 C 样式数组的标准容器? 例如,我有以下代码

int size = 5;    // This is not a constant in general
int *my_array = SpecialAllocationFunction(size);

我希望能够使用 C++ 标准样式容器访问此数组。具有迭代器和函数的东西,例如:sizebeginend、...

我知道如果my_array 的大小不变,我可以使用std::array。我自己也可以写一个,但我觉得肯定有现成的。

【问题讨论】:

  • std::vector 怎么样?
  • 那肯定是std::vector。你知道std::array,但不知道首选标准容器std::vector,我似乎很奇怪。
  • 您认为SpecialAllocationFunction 会做什么?如果它使用new[],那么它会在堆上分配。也许如果您尝试详细说明您遇到的实际问题,我们或许可以为您提供更好的帮助。
  • 如果您不想要副本,那么 gls::span 应该可以。
  • std::vector 可以与特殊分配器一起使用

标签: c++ arrays std


【解决方案1】:

使用自定义分配器,可以构建一个只有在运行时才知道大小的向量(因此不可能有std::array),包装现有数组。甚至可以通过覆盖特殊的construct 方法(*) 来保留预先存在的值。

这是一个可能的实现:

/**
 * a pseudo allocator which receives in constructor an existing array
 *  of a known size, and will return it provided the required size
 *  is less than the declared one. If keep is true in contructor, 
 *  nothing is done at object construction time: original values are
 *  preserved
 * at deallocation time, nothing will happen
 */
template <class T>
class SpecialAllocator {
    T * addr;
    size_t sz;
    bool keep;
public:
    typedef T value_type;
    SpecialAllocator(T * addr, size_t sz, bool keep):
        addr(addr), sz(sz), keep(keep) {}
    size_t max_size() {
        return sz;
    }
    T* allocate(size_t n, const void* hint=0) {
        if (n > sz) throw std::bad_alloc();  // throws a bad_alloc... 
        return addr;
    }
    void deallocate(T* p, size_t n) {}
    template <class U, class... Args>
    void construct(U* p, Args&&... args) {
        if (! keep) {
            ::new((void *)p) U(std::forward<Args>(args)...);
        }
    }
    template <class U>
    void destroy(U* p) {
        if (! keep) {
            p->~U();   // do not destroy what we have not constructed...
        }
    }

};

然后可以这样使用:

int size = 5;    // This is not a constant in general
int *my_array = SpecialAllocationFunction(size);

SpecialAllocator<int> alloc(my_array, size);
std::vector<int, SpecialAllocator<int> > vec(size, alloc);

从那时起,vec 将成为真正的 std::vector 包装 my_array

这是一个简单的代码演示:

int main(){
    int arr[5] = { 5, 4, 3, 2, 1 };
    SpecialAllocator<int> alloc(arr, 5, true); // original values will be preserved
    std::vector<int, SpecialAllocator<int> > vec(5, alloc);
    for(auto it= vec.begin(); it != vec.end(); it++) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    try {
        vec.push_back(8);
    }
    catch (std::bad_alloc& a) {
        std::cout << "allocation error" << std::endl;
    }
    return 0;
}

会成功输出:

5 4 3 2 1 
allocation error

(*)注意: 构造/破坏可能涉及不同的地方:push_backemplace_back 等。请仔细考虑您的实际用例在使用无操作 constructdestroy 方法之前。

【讨论】:

  • 如果arr 在创建vec 之前有值(而不是之后分配),我可以使用vec 访问它们吗?
  • @Tohiko:不是我的原始代码,因为 construct 方法没有被覆盖。但只要看看编辑过的
【解决方案2】:

正如@NathanOliver 和@utnapistim 在 cmets 中所说,gsl::span 有效。由于我不想包含这个库,我最终自己编写了一个“简单的包装器”。包括在下面,供其他人寻找答案(以及我未来的自己)

template<class T>
class span {
public:
    inline span() : _data(0), _size(0) {}
    inline span(T* d, size_t s) : _data(d), _size(s) {}
    inline T& operator[](size_t index) { return _data[index]; }
    inline const T& operator[](size_t index) const { return _data[index];}
    inline size_t size() const { return _size; };
    inline T* begin() { return _data; }
    inline const T* begin() const { return _data; }

    inline T* end() { return _data+_size; }
    inline const T* end() const { return _data+_size; }
protected:
    T* _data;
    size_t _size;
};

【讨论】:

  • inline 在这里是多余的。
猜你喜欢
  • 1970-01-01
  • 2018-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多