Ошибка сегментации, перегрузка ostream (‹‹)

Итак, я практикую кодирование на С++ и пытаюсь написать класс для матриц (хранящихся в виде массивов) с соответствующими перегруженными операциями.

Мне удалось определить класс и попытаться перегрузить оператор ‹‹, но мой текущий код вызывает ошибку сегментации (я компилирую с помощью g++ в Ubuntu). Я посмотрел в Интернете, и люди с похожими проблемами, как правило, всегда забывают возвращать os в своей функции перегрузки, но я сделал это, поэтому понятия не имею, в чем может быть моя проблема. Кроме того, мой перегруженный оператор сработает несколько раз, прежде чем вызовет ошибку сегментации.

Любая помощь будет принята с благодарностью.

Вот мой код:

#include<iostream>
#include<stdlib.h> // for c style exit
using namespace std;

class matrix
{
  // Friends
  friend ostream & operator<<(ostream &os, const matrix &mat);
  friend istream & operator>>(istream &is, matrix &mat);

private:
  double *mdata;
  int rows,columns;
public:
  // Default constructor
  matrix(){mdata=0; rows=columns=0;}
  // Parameterized constructor
  matrix(int m, int n){mdata = new double[ m*n ]; rows = m; columns = n;}
  // Copy constructor
  matrix(matrix &mat)
  // Destructor
  ~matrix(){delete[] mdata; cout<<"Destructing array."<<endl;}
  // Access functions
  int getrows() const {return rows;} // Return number of rows
  int getcols() const {return columns;} // Return number of columns
  int index(int m, int n) const // Return position in array of element (m,n)
  {
    if(m>0 && m<=rows && n>0 && n<=columns) return (n-1)+(m-1)*columns;
    else {cout<<"Error: out of range"<<endl; exit(1);}
  }
  double & operator()(int m, int n)const {return mdata[index(m,n)];}
  // Other access functions go here
  double & operator[](int i) {return mdata[i];}
  // Other functions 
  // Copy  Assignment operator
  matrix & operator=(matrix &mat);
};

// Member functions defined outside class
matrix::matrix(matrix &mat){
  rows = mat.getrows();
  columns = mat.getcols();
  for(int j = 0; j<rows*columns; j++){mdata[j] = mat[j];}
 }

matrix & matrix::operator=(matrix &mat){
  if (&mat == this) return *this;

  delete[] mdata; rows = 0; columns = 0;

  rows = mat.getrows(); columns = mat.getcols();
  if(rows>0&&columns>0){
    mdata = new double[(columns-1) + (rows-1)*columns + 1];
    for(int j = 0; j<rows*columns; j++){mdata[j] = mat[j];}
  }
  return *this;
}


// Overload insertion to output stream for matrices
ostream & operator<<(ostream &os, const matrix &mat){
  for(int j = 0;j<mat.rows;j++){
    for(int k = 0;k<mat.columns;k++){
      os << mat(j+1,k+1) << " ";
   }
    os << endl;
 }
  return os;
}

// Main program

int main(){

  // Demonstrate default constructor
  matrix a1;
  cout<<a1;

  // Parameterized constructor
  const int m(2),n(2);
  matrix a2(m,n);
  // Set values for a2 here
  a2[0] = 1; a2[1] = 2; a2[2] = 3; a2[3] = 4;
  // Print matrix a2
  cout<<a2;


  // Deep copy by assignment: define new matrix a3 then copy from a2 to a3
  matrix a3(m,n);
  cout<<a3;
  a3=a2;
  cout<<a3;
  // Modify contents of original matrix and show assigned matrix is unchanged here
  a2[0] = 5;
  cout<<a2;
  cout<<a3; //here is where segmentation fault occurs
  return 0;
}

person user7631642    schedule 06.03.2017    source источник
comment
Я предлагаю сначала исправить конструктор копирования и оператор присваивания.   -  person juanchopanza    schedule 07.03.2017
comment
Затем запустите отладчиком, потому что ваш код полон тривиальных ошибок.   -  person juanchopanza    schedule 07.03.2017
comment
Я дошел до определения класса — вы этого не сделали. Этот матричный класс может развалиться с помощью всего лишь двухстрочной программы main(). { matrix m(1,2); matrix m2=m;}   -  person PaulMcKenzie    schedule 07.03.2017
comment
Я не могу воспроизвести ошибку (после того, как я добавил точку с запятой, необходимую компилятору). Это минимальный пример?   -  person Beta    schedule 07.03.2017
comment
@user7631642 user7631642 Уже говорилось, что у вас большая проблема с конструктором копирования. Разве вы этого не видите? У вас также есть вырисовывающаяся ошибка в вашем операторе присваивания. matrix m; matrix m2(1,2); m = m2; Если я смогу создать хаос с помощью этих крошечных примеров, возможно, вам следует сначала исправить эти проблемы.   -  person PaulMcKenzie    schedule 07.03.2017
comment
@PaulMcKenzie Ну, я явно новичок, и если бы я мог это видеть, я бы это исправил. Пожалуйста, просветите меня.   -  person user7631642    schedule 07.03.2017
comment
@user7631642 user7631642 -- В конструкторе копирования вы не выделили памяти для матрицы, в которую копируете. Это должно было быть очевидно, учитывая то, что вы уже написали (и это не код для начинающих). Это проявляется в маленькой двухстрочной программе, которую я написал (почему бы не запустить ее, просто посмотреть, что может произойти?).   -  person PaulMcKenzie    schedule 07.03.2017
comment
@PaulMcKenzie Понятно, понял. Спасибо.   -  person user7631642    schedule 07.03.2017
comment
@user7631642 user7631642 Кроме того, ваш оператор присваивания можно было легко написать так: {matrix tm(mat); std::swap(tm.mdata,data);std::swap(tm.rows,rows);std::swap(tm.columns,columns);return *this;}. Это позволяет избежать ошибок в вашем операторе присваивания (у вас их несколько). Кроме того, не вызывайте exit(1) в середине кода класса. см. это   -  person PaulMcKenzie    schedule 07.03.2017


Ответы (2)


Кажется, вы выходите за пределы своей матрицы. Вы должны удалить +1 для i и j...

for(int j = 0;j<mat.rows;j++){
    for(int k = 0;k<mat.columns;k++){
      os << mat(j,k) << " ";
   }

Когда массив состоит из 3 элементов, это означает, что вы можете получить доступ к этим 3 элементам:

array[0]
array[1]
array[2]

но не array[3], который является 4-м элементом вашего массива, длина которого равна 3.

Как правило, когда вы получаете segmentation fault, вы должны запускать свою программу с gdb или valgrind. Эти инструменты часто дадут вам очень ценную информацию, чтобы определить основную причину ошибок доступа к памяти в вашем коде.

person SegFault    schedule 06.03.2017
comment
Длина массива равна 4. Это подтверждается тем фактом, что ostream используется многократно, прежде чем произойдет ошибка сегментации и выдаст, как и ожидалось, 4 элемента. - person user7631642; 07.03.2017
comment
Я написал 3 в качестве примера. Если длина вашего массива равна 4, вы пытаетесь получить доступ к 5-му элементу (array[4]). Ошибки сегментации не являются автоматическими, т.е. ваша программа может запускаться несколько раз без сбоев, это не значит, что вы не делаете что-то опасное. Это зависит от многих условий, выходящих за рамки этого вопроса. - person SegFault; 07.03.2017

Вам нужно сделать ФУНКЦИЮ friend матрицы классов, поскольку она не может получить доступ к columns или rows, поскольку они являются закрытыми членами. Типичная ошибка при перегрузке operator<< и operator>>. Никогда не забывайте friend.

person jiveturkey    schedule 06.03.2017
comment
На самом деле довольно редко оператору ‹‹ нужно быть другом, и в данном случае это не так, потому что у класса есть подходящие функции доступа, чтобы реализовать его как обычную свободную функцию. - person ; 07.03.2017
comment
Я пропустил декларации, извиняюсь. - person jiveturkey; 07.03.2017