Распределение векторов (или векторов векторов) динамически

Мне нужно динамически выделять 1-D и 2-D массивы, размеры которых заданы во время выполнения.

Мне удалось "обнаружить" std::vector, и я думаю, что это соответствует моим целям, но я хотел бы спросить, правильно ли я написал и/или что можно улучшить.

Это то, что я делаю:

#include <vector>

typedef std::vector< std::vector<double> > matrix;

//... various code and other stuff

std::vector<double> *name = new std::vector<double> (size);
matrix *name2 = new matrix(sizeX, std::vector<double>(sizeY));

Ответы

Ответ 1

Как вы обнаружили, динамическое распределение массивов требуется, когда ваши измерения задаются во время выполнения.

Однако std::vector уже является оберткой вокруг этого процесса, поэтому динамическое распределение векторов похоже на двойной положительный результат. Это избыточно.

Просто напишите (С++ 98):

#include <vector>

typedef std::vector< std::vector<double> > matrix;
matrix name(sizeX, std::vector<double>(sizeY));

или (С++ 11 и более поздние версии):

#include <vector>

using matrix = std::vector<std::vector<double>>;
matrix name(sizeX, std::vector<double>(sizeY));

Ответ 2

Вы объединяете два вопроса: динамическое размещение и изменяемые размеры контейнеров. Вам не нужно беспокоиться о динамическом распределении, так как ваш контейнер делает это уже для вас, поэтому просто скажите следующее:

matrix name(sizeX, std::vector<double>(sizeY));

Это сделает name объект с автоматическим временем хранения, и вы можете получить доступ к его членам через name[i][j].

Ответ 3

То, что вы делаете, должно работать в основном:

В общем случае динамически не выделяйте объекты

Если вы хотите вектор, сделайте следующее:

std::vector<double> vec(size);

не это:

std::vector<double>* vec = new std::vector<double>(size);

Последний дает вам указатель, который вы должны удалить. Первый дает вам вектор, который, когда он выходит за рамки, очищается после себя. (Внутренне, конечно, он динамически выделяет объекты, но фокус в том, что это обрабатывается самим классом, и вам не нужно беспокоиться об этом в своем пользовательском коде).

Ответ 4

Это правильно, но может быть сделано более эффективно.

Вы можете использовать многомерные массивы boost: http://www.boost.org/doc/libs/1_47_0/libs/multi_array/doc/user.html

Или вы можете реализовать свой собственный класс и самостоятельно обрабатывать индексирование. Возможно, что-то вроде этого (что плохо проверено):

#include <vector>
#include <cassert>
template <typename T, typename A = std::allocator<T> >
class Array2d
{
public:
    typedef Array2d<T> self;
    typedef std::vector<T, A> Storage;

    typedef typename Storage::iterator       iterator;
    typedef typename Storage::const_iterator const_iterator;
    Array2d() : major_(0), minor_(0) {}
    Array2d(size_t major, size_t minor)
        : major_(major)
        , minor_(minor)
        , storage_(major * minor)
    {}

    template <typename U>
    Array2d(size_t major, size_t minor, U const& init)
        : major_(major)
        , minor_(minor)
        , storage_(major * minor, u)
    {
    }
    iterator begin()                { return storage_.begin(); }
    const_iterator begin() const    { return storage_.begin(); }
    iterator end()                  { return storage_.end(); }
    const_iterator end() const      { return storage_.end(); }
    iterator begin(size_t major) {
        assert(major < major_);
        return storage_.begin() + (major * minor_);
    }
    const_iterator begin(size_t major) const {
        assert(major < major_);
        return storage_.begin() + (major * minor_);
    }
    iterator end(size_t major) {
        assert(major < major_);
        return storage_.begin() + ((major + 1) * minor_);
    }
    const_iterator end(size_t major) const {
        assert(major < major_);
        return storage_.begin() + ((major + 1) * minor_);
    }
    void clear() {
        storage_.clear();
        major_ = 0;
        minor_ = 0;
    }
    void clearResize(size_t major, size_t minor)
    {
        clear();
        storage_.resize(major * minor);
        major_ = major;
        minor_ = minor;
    }
    void resize(size_t major, size_t minor)
    {
        if ((major != major_) && (minor != minor_))
        {
            Array2d tmp(major, minor);
            swap(tmp);

            // Get minimum minor axis
            size_t const dist = (tmp.minor_ < minor_) ? tmp.minor_ : minor_;
            size_t m = 0;
            // copy values across
            for (; (m < tmp.major_) && (m < major_); ++m) {
                std::copy(tmp.begin(m), tmp.begin(m) + dist, begin(m));
            }
        }
    }
    void swap(self& other)
    {
        storage_.swap(other.storage_);
        std::swap(major_, other.major_);
        std::swap(minor_, other.minor_);
    }
    size_t minor() const {
        return minor_;
    }
    size_t major() const {
        return major_;
    }
    T*       buffer()       { return &storage_[0]; }
    T const* buffer() const { return &storage_[0]; }
    bool empty() const {
        return storage_.empty();
    }
    template <typename ArrRef, typename Ref>
    class MajorProxy
    {
        ArrRef arr_;
        size_t major_;

    public:
        MajorProxy(ArrRef arr, size_t major)
        : arr_(arr)
        , major_(major)
        {}

        Ref operator[](size_t index) const {
            assert(index < arr_.minor());
            return *(arr_.buffer() + (index + (major_ * arr_.minor())));
        }
    };
    MajorProxy<self&, T&>
    operator[](size_t major) {
        return MajorProxy<self&, T&>(*this, major);
    }
    MajorProxy<self const&, T const&>
    operator[](size_t major) const {
        return MajorProxy<self&, T&>(*this, major);
    }
private:
    size_t major_;
    size_t minor_;
    Storage storage_;
};

Ответ 5

В то время как точки, которые были сделаны другими ответами, были очень правильными (не динамически выделять вектор через новый, а скорее переносить вектор), если вы думаете о терминах векторов и матриц (например, линейная алгебра), вы может захотеть использовать библиотеку Eigen-матриц.

Ответ 6

Вы не распределяете контейнеры динамически. Они могут автоматически управлять памятью для вас, если они сами не управляются вручную.

Когда вы добавляете новые элементы с push_back (или insert), вектор растет, вы можете выбрать его размер с самого начала с помощью аргументов конструктору, и позже вы можете изменить его размер с помощью метода resize.

Создание вектора векторов с вашими размерами с помощью конструктора выглядит так:

std::vector< std::vector<double> > matrix(size, std::vector<double>(sizeY));

Это означает: size экземпляры a std::vector<double>, каждый из которых содержит sizeY удваивает (инициализируется до 0.0).

Ответ 7

Если вам не нужно изменять размеры массива во время выполнения, вы можете просто использовать стандартные массивы (выделенные во время выполнения)!

Однако, если вам нужно изменить размеры массивов во время выполнения, вы можете использовать следующий (пересмотренный) код:

#include <vector>

typedef std::vector< std::vector<double> > matrix;

//... various code and other stuff

std::vector<double> *name = new std::vector<double> (size);

matrix *name2 = new matrix(sizeX, std::vector<double>(sizeY));

В сущности, все, что я сделал, это удалить одну скобку (().