Friday, November 22, 2019

Многомерные массивы в си++

Мы знаем, что по причине обратной совместимости с си, массивы в си++ представлены двумя вариантами, базовым и из стандартной библиотеки. Базовый вариант имеет известные недостатки, поэтому сегодня мы поговорим о варианте из стандартной библиотеки в контексте многомерных массивов. В частности, мы рассмотрим создание и работу с двумерным массивом с использованием классов std::array и std::vector.
 
#include <iostream>
#include <iomanip>
#include <vector>
#include <array>

Двумерный массив удобно представлять в виде массива строк, в котором каждая строка также является массивом элементов array[row][col]. Если представить такой массив графически, то получим таблицу, со строками по горизонтали и столбцами по вертикали. Обход элементов, можно осуществить с помощью вложенного цикла.

//access
template <typename T>
void printArray(T _array) {
    std::cout << "-------------------------------" << std::endl;
    int i = 1;
    for (const auto &row_in_arr : _array) {
        std::cout << "row" << i++ << ": ";
        for (const auto &col_in_row : row_in_arr) {
            std::cout << "[ " << std::setw(2) << std::setfill('0') << col_in_row << " ]";
        }
        std::cout << std::endl;
    }
}

Класс array служит для организации данных в группу элементов фиксированной длины, имеет метод возвращающий размер группы size(), а также метод доступа к элементу по индексу, проверяющий выход за границы массива at(index).
 
int main() {
    //array of arrays
    auto myArr1 = std::array<std::array<int, 5>, 5>();
   
    printArray(myArr1);
    //-------------------------------
    //row1: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row2: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row3: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row4: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row5: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]

    //filling
    int x = 1;
    for (auto &row_in_arr : myArr1) {
        for (auto &col_in_row : row_in_arr) {
            col_in_row = x++;
        }
    }
   
    printArray(myArr1);
    //-------------------------------
    //row1: [ 01 ][ 02 ][ 03 ][ 04 ][ 05 ]
    //row2: [ 06 ][ 07 ][ 08 ][ 09 ][ 10 ]
    //row3: [ 11 ][ 12 ][ 13 ][ 14 ][ 15 ]
    //row4: [ 16 ][ 17 ][ 18 ][ 19 ][ 20 ]
    //row5: [ 21 ][ 22 ][ 23 ][ 24 ][ 25 ]
   
    //size
    std::cout << "size: " << myArr1.size() * myArr1.at(0).size() << std::endl;
    //size: 25
   
    //indexes
    std::cout << "row:1 col:1 " << "[ " << myArr1[0][0] << " ]" << std::endl;
    std::cout << "row:5 col:5 " << "[ " << myArr1[4][4] << " ]" << std::endl;
    //row:1 col:1 [ 1 ]
    //row:5 col:5 [ 25 ]
   
    //range
    try {
        std::cout << "row:6 col:6 " << ">>> " << myArr1.at(5).at(5) << std::endl;
    } catch (std::out_of_range &e) {
        std::cout << "out_of_range!!!" << std::endl;
    }
    //row:6 col:6 >>> out_of_range!!!

Класс vector дополняет вышесказанное возможностью изменять размер массива, например при помощи метода добавления нового элемента в конец массива push_back(obj). Так как при использовании этого метода происходит копирование, можно использовать метод emplace_back(obj), конструирующий новый объект.
 
    //vector of vectors
    auto myArr2 = std::vector<std::vector<int>>(5, std::vector<int>(5, 0));
   
    printArray(myArr2);
    //-------------------------------
    //row1: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row2: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row3: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row4: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row5: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]

    //add row
    myArr2.push_back(std::vector<int>(4,1));
    //construct row
    //myArr2.emplace_back(4,1);
   
    //add element
    myArr2.at(5).push_back(10);
   
    printArray(myArr2);
    //-------------------------------
    //row1: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row2: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row3: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row4: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row5: [ 00 ][ 00 ][ 00 ][ 00 ][ 00 ]
    //row6: [ 01 ][ 01 ][ 01 ][ 01 ][ 10 ]

    //access
    std::cout << "row:1 col:1 " << "[ " << myArr2[0][0] << " ]" << std::endl;
    std::cout << "row:6 col:5 " << "[ " << myArr2.at(5).at(4) << " ]" << std::endl;
    //row:1 col:1 [ 0 ]
    //row:6 col:5 [ 10 ]

    return 0;
}

Нужно помнить, что индексы элементов в массиве начинаются с 0 (ноль).
 
Чем же отличаются std::array и std::vector? Тем, что массив хранит свои элементы в стеке, а вектор в куче. Поэтому массив быстрее, т.к. не тратит время на аллокацию и деаллокацию.

Перегуд В.

No comments: