半动态多维存储对象

Half-dynamic multidimensional storage object

我正在寻找一个 C++ 多维存储对象,它将其数据存储在一个连续的内存块中,并且在其中一个维度上是动态的。你能推荐我如何处理这个问题吗?

示例:

std::vector<std::array<int, 250>> pixelsMarker; 
// The array stores its data in a continuous block
// but the vector data not continuous, because
// an std::array is a complex object

std::vector<int[250]> pixelsMarker
// This is an invalid definition of a vector

为什么不创建自己的数据访问器而只使用一个数组?

例如:

std::vector<int> myArray(250*100);

在这里创建 100 个数组,每个数组的大小为 250(这是一个大小为 250*100 的单个数组)。要访问数组 N 中的元素,请使用函数

int accessor(int N, int elementNum)
{
    return N*250 + elementNum;
}

这将为您提供第 N 个数组中的元素 elementNum

当然,您始终可以选择继承 std::vector 或创建您自己的 class 来为您执行此操作。

为了使 N 维 std::array 成为一个连续的块,std::array 必须没有额外的开销。 std::array 中唯一应该包含的数据是数组。关于数组的所有数据都由模板固定。

所以如果

std::array<std::array<int, 250>, 100> data;

然后是连续的

std::vector<std::array<int, 250>> data;

也将是连续的。

就是说,我在最近免费提供的标准草案(没有官方副本)中找不到(或解释)任何保证实现不能添加另一个或两个成员的内容只是为了给大家毁了它,但为什么要做这样的事情?

这是一个可能的实现,使用 std::vector 作为 row-major 配置中的底层容器类型,其中可以使用 [=13= 在 run-time 更改行数]、semi_dynamic_matrix::erase_rowsemi_dynamic_matrix::pop_rowsemi_dynamic_matrix::rows_resize.

template<typename Ty,
    class Allocator = std::allocator<Ty>
> class semi_dynamic_matrix {
    class proxy_row_vector { // use to allow [][] usage on semi_dynamic_matrix objects
    public:
        proxy_row_vector(std::vector<Ty, Allocator>& _vec, std::size_t i, std::size_t cols) 
            : vec(_vec), row_ind(i), cols_(cols) {}
        const Ty& operator[](std::size_t j) const {
            return vec[row_ind*cols+j];
        }
        Ty& operator[](std::size_t j) {
            return vec[row_ind*cols+j];
        }
    private:
        std::vector<Ty, Allocator>& vec;
        std::size_t row_ind;
        std::size_t cols_;
    };
public:
    // PUBLIC TYPEDEFS
    typedef Ty value_type;
    typedef Ty& reference;
    typedef const Ty& const_reference;
    typedef Ty* pointer;
    typedef const Ty* const_pointer;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;
    typedef Allocator allocator_type;
    typedef typename std::vector<Ty,Allocator>::iterator iterator;
    // and similar for const_iterator, reverse_iterator...
    // CONSTRUCTION/ASSIGNMENT
    // default constructor
    semi_dynamic_matrix() : semi_dynamic_matrix(Allocator()) {}
    explicit semi_dynamic_matrix(const Allocator& alloc)
        : mtx(alloc), rows_(0U), cols_(0U) {}
    // construct matrix of size rows*cols
    explicit semi_dynamic_matrix(size_type rows, size_type cols, const Allocator& alloc = Allocator())
        : mtx(rows*cols, alloc), rows_(rows), cols_(cols) {}
    // other constructors (copy, move, ...)
    // CAPACITY
    size_type rows() const noexcept { return rows_; }
    constexpr size_type columns() const noexcept { return cols_; }
    // ELEMENT ACCESS
    proxy_row_vector operator[](size_type i) const {
        return proxy_row_vector(mtx, i, cols_);
    }
    proxy_row_vector operator[](size_type i) {
        return proxy_row_vector(mtx, i, cols_);
    }
    // other accessors, e.g. at(), front(), ...
    // ITERATORS
    iterator begin() noexcept { return mtx.begin(); }
    iterator end() noexcept { return mtx.end(); }
    // similarly for cbegin(), cend(), rbegin(), ...
    // MODIFIERS
    iterator insert_row(size_type row_pos, const value_type& val) {
        return insert_row(row_pos, std::vector<value_type>(cols_, val));
    }
    iterator insert_row(size_type row_pos, const std::vector<value_type>& row_vec) {
        if (row_pos > rows_) throw std::out_of_range("message");
        if (row_vec.size() != cols_) throw std::invalid_argument("message");
        ++rows_;
        return mtx.insert(mtx.begin()+row_pos*cols_, row_vec.begin(), row_vec.end());
    }
    iterator insert_row(size_type row_pos, std::vector<value_type>&& row_vec =
        std::vector<value_type>()) {
        if (row_pos > rows_) throw std::out_of_range("message");
        if (row_vec.size() > cols_) throw std::invalid_argument("message");
        ++rows_;
        if (row_vec.size() < cols_) row_vec.resize(cols_);
        return mtx.insert(mtx.begin()+row_pos*cols_, std::make_move_iterator(
            row_vec.begin()), std::make_move_iterator(row_vec.end()));
    }
    iterator erase_row(size_type row_pos) {
        if (!(row_pos < rows_)) throw std::out_of_range("");
        --rows_;
        return mtx.erase(mtx.begin()+row_pos*cols_, mtx.begin()+cols_*(row_pos+1));
    }
    void pop_row() {
        for (size_type i = 0; i < cols_; ++i) mtx.pop_back();
        --rows_;
    }
    void rows_resize(size_type rows) {
        size_type tmp_rows = rows_;
        if (rows == rows_) return;
        if (rows > rows_) { // expand
            for (size_type i = 0; i < (rows_-tmp_rows); ++i)
                insert_row(rows_, std::move(std::vector<value_type>(cols_)));
        }
        else { // contract
            for (size_type i = 0; i < (tmp_rows-rows); ++i) pop_row();
        }
    }
private:
    std::vector<value_type, allocator_type> mtx;
    size_type rows_;
    size_type cols_;
};

那你就可以这样用了,

semi_dynamic_matrix<int> sdm(3,3); // create matrix of size 3x3
sdm.rows_resize(5); // resize matrix to 5x3
sdm.erase_row(0); // erase first row of matrix, size is now 4x3

底层迭代器类型仅使用 std::vector 迭代器,因此您可以在此 class 上执行任何典型的 <algorithm> header(和其他)操作。

或者,如果您想要一个在行和列中都是动态的矩阵结构,那么请考虑使用我刚才创建的实现:dynamic_matrix.