Помогите исправить код...

Код должен вычислять ср. значение 3 массивов, но при вводе размера массива где есть 2 (Например: Строк-2, столбцов 1), выдает ошибку:

#include<math.h>
#include<iostream>
#include <iomanip>
#include<ctime>

using namespace std;
void main() {
    setlocale(LC_ALL,"Russian");
int n, m, g, h, k, l; double sr_gnach1=0;//обьявление переменных
//первый массив
///////////////////////////////////////////////
cout << "Введите кол-во строк массива: ";
cin >> n;
cout << "Введите кол-во столбцов массива: ";
cin >> m;
//выделение динамической памяти для массива
double **dArr = new double *[n];
for (int i = 0; i < n; i++)
    dArr[i] = new double [m];
//заполнение массива
srand(time(NULL));
for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        dArr[i][j] = rand() % 1000 / (double) 100;
    }
}
//вывод массива на экран
cout << "весь массив: " << endl;
for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        cout << setw(7) << dArr[i][j];
    }
    cout << endl;
}
cout<<endl;
//второй массив
///////////////////////////////////////////////
cout << "Введите кол-во строк массива: ";
cin >> g;
cout << "Введите кол-во столбцов массива: ";
cin >> h;
//выделение динамической памяти для массива
double **dArr1 = new double *[g];
for (int i = 0; i < g; i++)
    dArr1[i] = new double [h];
//заполнение массива
//srand(time(NULL));
for (int i = 0; i < g; i++) {
    for (int j = 0; j < h; j++) {
        dArr1[i][j] = rand() % 1000 / (double) 100;
    }
}

cout << "весь массив: " << endl;
for (int i = 0; i < g; i++) {
    for (int j = 0; j < h; j++) {
        cout << setw(7) << dArr1[i][j];
    }
    cout << endl;
}
cout<<endl;
//третий массив
///////////////////////////////////////////////

cout << "Введите кол-во строк массива: ";
cin >> k;
cout << "Введите кол-во столбцов массива: ";
cin >> l;
//выделение динамической памяти для массива
double **dArr2 = new double *[k];
for (int i = 0; i < k; i++)
    dArr2[i] = new double [l];
//заполнение массива
for (int i = 0; i < k; i++) {
    for (int j = 0; j < l; j++) {
        dArr2[i][j] = rand() % 1000 / (double) 100;
    }
}
//заполнение массива
cout << "весь массив: " << endl;
for (int i = 0; i < k; i++) {
    for (int j = 0; j < l; j++) {
        cout << setw(7) << dArr2[i][j];
    }
    cout << endl;
}
for(int i=0;i<n;i++){
    for(int j=0;j<m;j++){
    sr_gnach1+=dArr[i][j];

    }
}//cout << sr_gnach1<<endl;
for(int i=0;i<g;i++){
    for(int j=0;j<h;j++){
    sr_gnach1+=dArr1[i][j];

    }
}//cout << sr_gnach1<<endl;
for(int i=0;i<k;i++){
    for(int j=0;j<l;j++){
    sr_gnach1+=dArr2[i][j];

    }
}
///cout << sr_gnach1<<endl;
cout <<"среднее значение элементов"<<" = ";
cout << sr_gnach1 / ((n*m) + (g*h) + (k*l)) << endl;

for (int i=0;i<m;i++){
    delete dArr[i];
}
delete dArr;

for (int i=0;i<h;i++){
    delete dArr1[i];
}
delete dArr1;

for (int i=0;i<l;i++){
    delete dArr2[i];
}
delete dArr2;
    system("pause");    
}

Спасибо, модератор, что поправил оформление кода. А то совсем тоскливо, когда движок форума еще «ошибок» добавляет.

Modern, во-первых, удалять массивы нужно через delete[].
Во-вторых, ты перепутал букавки, которые у тебя определяют размеры массивов.

    ///cout << sr_gnach1<<endl;
    cout << "среднее значение элементов" << " = ";
    cout << sr_gnach1 / ((n*m) + (g*h) + (k*l)) << endl;

    for (int i = 0; i < n; i++) {
        delete[] dArr[i];
    }
    delete[] dArr;

    for (int i = 0; i < g; i++) {
        delete[] dArr1[i];
    }
    delete[] dArr1;

    for (int i = 0; i < k; i++) {
        delete[] dArr2[i];
    }
    delete[] dArr2;

Вообще, все эти действия с массивами лучше оформить через функции. Тогда и кода будет меньше, и отлаживать проще.
А еще лучше завернуть массив в класс. Тогда голова не будет болеть и о том, какие размеры у того или другого массива.

На досуге прикинул как это будет выглядеть с классом. Получилось довольно длинно. Но зато по честному. Для этой конкретной задачи половину методов можно было не писать.
Для трассировки добавил макрос TR.

#include <iostream>
#include <iomanip>
#include <ctime>
#include <stdexcept>
#include <vector>
#include <algorithm>

using namespace std;

// для отключения трассировки закомментировать следующую строку
#define TRACE

#ifdef TRACE
#define TR(x)   cout << "*** " << (x) << endl;
#else
#define TR(x)
#endif

class Matrix {
public:
    Matrix(size_t row, size_t col) : _row(row), _col(col) {
        TR("Matrix(size_t row, size_t col)");
        create();
    }

    Matrix(const Matrix & m) : _row(m._row), _col(m._col) {
        TR("Matrix(const Matrix & m)");
        create();
        copy(m);
    }

    Matrix(Matrix && m) : _row(m._row), _col(m._col) {
        TR("Matrix(Matrix && m)");
        _arr = m._arr;
        m._arr = nullptr;
        m._row = m._col = 0;
    }

    ~Matrix() {
        TR("~Matrix()");
        destroy();
    }

    Matrix & operator = (const Matrix & m) {
        TR("Matrix & operator = (const Matrix & m)");
        if (this != &m) {
            destroy();
            _row = m._row;
            _col = m._col;
            create();
            copy(m);
        }
        return *this;
    }

    Matrix & operator = (Matrix && m) {
        TR("Matrix & operator = (Matrix && m)");
        if (this != &m) {
            destroy();
            _row = m._row;
            _col = m._col;
            _arr = m._arr;
            m._arr = nullptr;
            m._row = m._col = 0;
        }
        return *this;
    }

    size_t rows() const {
        return _row;
    }

    size_t cols() const {
        return _col;
    }

    size_t nelems() const {
        return _row * _col;
    }

    double & at(size_t row, size_t col) {
        if (row >= _row || col >= _col) {
            throw out_of_range("matrix: index out of range");
        }
        return _arr[row][col];
    }

    double sum() const {
        double _sum = 0.0;
        for (size_t i = 0; i < _row; ++i) {
            for (size_t j = 0; j < _col; ++j) {
                _sum += _arr[i][j];
            }
        }
        return _sum;
    }

    void set_values(double(*f)()) {
        for (size_t i = 0; i < _row; ++i) {
            for (size_t j = 0; j < _col; ++j) {
                _arr[i][j] = f();
            }
        }
    }

    friend ostream & operator << (ostream & os, const Matrix & m);

private:
    size_t _row, _col;
    double **_arr;

    void destroy() {
        if (_arr != nullptr) {
            for (size_t i = 0; i < _row; ++i) {
                delete[] _arr[i];
            }
            delete[] _arr;
        }
    }

    void create() {
        _arr = new double *[_row];
        for (size_t i = 0; i < _row; ++i) {
            _arr[i] = new double[_col];
        }
    }

    void copy(const Matrix & m) {
        for (size_t i = 0; i < _row; ++i) {
            for (size_t j = 0; j < _col; ++j) {
                _arr[i][j] = m._arr[i][j];
            }
        }
    }
};

ostream & operator << (ostream & os, const Matrix & m) {
    for (size_t i = 0; i < m._row; ++i) {
        for (size_t j = 0; j < m._col; ++j) {
            os << setw(7) << m._arr[i][j];
        }
        os << endl;
    }
    return os;
}

double rand_val() {
    return rand() % 1000 / (double)100;
}

const char *msg_row = "Введите кол-во строк массива:    ";
const char *msg_col = "Введите кол-во столбцов массива: ";

struct Dims {
    size_t r, c;
};

const size_t ARR_NUM = 3;

int main() {
    setlocale(LC_ALL, "Russian");
    Dims dims[ARR_NUM];

    for (size_t i = 0; i < ARR_NUM; ++i) {
        cout << "Массив #" << i + 1 << ":\n";
        cout << msg_row;
        cin >> dims[i].r;
        cout << msg_col;
        cin >> dims[i].c;
    }

    // начало

    Matrix m1(dims[0].r, dims[0].c), m2(dims[1].r, dims[1].c), m3(dims[2].r, dims[2].c);

    srand(0);  // фиксируем рандомайзер для отладки

    m1.set_values(rand_val);
    m2.set_values(rand_val);
    m3.set_values(rand_val);

    cout << "массив 1:\n" << m1 << endl <<
        "массив 2:\n" << m2 << endl <<
        "массив 3:\n" << m3 << endl;

    cout << "среднее значение элементов = " << (m1.sum() + m2.sum() + m3.sum()) / (m1.nelems() + m2.nelems() + m3.nelems()) << endl;

    // конец

    return 0;
}

Это первый вариант.

А вот второй вариант для функции main. Им нужно заменить код между «началом» и «концом»:

    vector<Matrix> vm;
    vm.reserve(ARR_NUM);   // резервируем место для всех объектов Matrix

    srand(0);  // фиксируем рандомайзер для отладки

    for (size_t i = 0; i < ARR_NUM; ++i) {
        vm.emplace_back(dims[i].r, dims[i].c);
    }

    double total_sum = 0.0;
    size_t total_elems = 0;
    size_t cnt = 1;
    for_each(vm.begin(), vm.end(),
             [&total_sum, &total_elems, &cnt](Matrix & m) {
        m.set_values(rand_val);
        cout << "массив " << cnt++ << ":\n";
        cout << m << endl;
        total_sum += m.sum();
        total_elems += m.nelems();
    });

    cout << "среднее значение элементов = " << total_sum / total_elems << endl;

Интересно, что если в векторе не зарезервировать место под объекты, то при каждом следующем вызове emplace_back уже существущие в векторе объекты будут копироваться (т.е. вызывается конструктор копии для нового объекта, а затем деструктор для старого объекта со всеми вытекающими последствиями). Я как-то не ожидал такого поведения от вектора ((

Хотелось бы услышать замечания по качеству кода. (Знаю, что надо было бы разделить код на три файла: matrix.h, matrix.cpp и main.cpp. Но для краткости пусть будет так.)

Я как-то не ожидал такого поведения от вектора

А какое поведение ожидалось? Чувствую, что хотелось бы перемещение чтобы использовал вектор? Но тогда и код нужно писать правильно. Конструктор перемещения вектора будет перемещать элементы контейнера только в случае, если конструктор перемещения элементов гарантирует, что из него не вылетит исключение. Другими словами, сделай

Matrix(Matrix && m) noexcept

и не греши на вектор, он здесь не виноват.

Как в рекламе Сникерса: «Ну, что, лучше?.. Лучше.» Конструктор перемещения подхватился. На счет тонкости с noexcept не знал ((
Интересно, из каких соображений сделано такое ограничение. Как бы обычный конструктор и конструктор копии, которые могут выкидывать исключения, для контейнеров STL кошерно. А конструктор перемещения — нет.

А какое поведение ожидалось?

Вообще-то я ожидал большего, чем конструктор перемещения. Где-то читал, что при инициализации контейнера сразу выделяется память под несколько элементов (то ли 12, то ли 16), а потом, при добавлении, когда ёмкости не хватает, происходит увеличение ёмкости в 1.5 раза, соответственно с перераспределением памяти. Это раз. И еще почему-то думал (тоже вроде где-то читал), что в самом контейнере хранятся указатели на объекты нетривиальных типов. Т.е. при расширении контейнера копируются только указатели, а не сами объекты.

И еще почему-то думал (тоже вроде где-то читал), что в самом контейнере хранятся указатели на объекты нетривиальных типов. Т.е. при расширении контейнера копируются только указатели, а не сами объекты.

Такое распространено во многих фреймворках, например, владеющие контейнеры в C++Builder, но в стандартной библиотеке вектор хранит само объекты.

Интересно, из каких соображений сделано такое ограничение.

Для соблюдения требований строгой гарантии безопасности исключений. Если перемещающий конструктор выкидывает исключения, то такую гарантию соблюсти невозможно, но можно обеспечить такую гарантию с помощью копирования.

Всё равно не понятно. Что имеется ввиду под «строгой гарантии безопасности исключений»?

Мы добавляем объект в вектор. Вектор расширяется, при этом создаются копии старых элементов, а сами эти элементы уничтожаются. Копии элементов могут создаваться либо конструктором копирования, либо конструктором перемещения. Какая разница где возникнет исключение: в конструкторе копирования или в конструкторе перемещения? Причем интуиция подсказывает, что вероятность получить исключение больше именно в конструкторе копирования (при выделении памяти, например).

Что имеется ввиду под «строгой гарантии безопасности исключений»?

Оно и имеется ввиду. Это означает, что объект либо меняется полностью, либо не меняется вообще, если возникло исключение. Если конструктор перемещения объекта в векторе может выбросить исключение, то такую гарантию дать невозможно, поэтому используется копирование.

Какая разница где возникнет исключение: в конструкторе копирования или в конструкторе перемещения?

Огромная. Если используется перемещение, то часть объектов опустошена при переносе, а часть — нет. Это нарушает гарантии. При копировании, если выбросилось исключение, мы можем просто почистить всё и выбросить его дальше, при этом вектор останется не измененный, соблюдая гарантию.

Причем интуиция подсказывает, что вероятность получить исключение больше именно в конструкторе копирования (при выделении памяти, например).

При копировании плевать вылетит исключение или нет. Вектор может обеспечить строгую гарантию. А вектору откуда знать вылетит исключение из конструктора перемещения или нет? Если потенциально оно может оттуда вылететь, то значит перемещение использовать нельзя, т.к. если исключение вылетит, то строгая гарантия будет нарушена. А как узнать может ли вылететь исключение из конструктора перемещения? Правильно, посмотреть на noexcept.

Понял. Спасибо за разъяснение.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.