0

メソッドからオブジェクトを返した後matrix matrix::operator+(const matrix& right)、配列全体が消去されたようです! 次の単純化された例で、この奇妙な問題を説明しましょう。

main.cpp:

#include "matrix.h"
#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    matrix a("a.mt");
    matrix b("b.mt");

    matrix d(a.getRows(),a.getColumns());

    d = a+b;

    std::cout<<"hooray!";
    return 0;
}

マトリックス.h:

#ifndef H_MATRIX
#define H_MATRIX

#include <string>

struct field
{
    int row; 
    int column;
    double value;
};

class matrix
{
private:
    int c; //columns
    int r; //rows

    field** b; //2d array
    void allocmem();
public:
    matrix(int rows,int columns);
    matrix(std::string filename);
    ~matrix();

    void read(std::string fname);
    matrix operator+(const matrix& right);
    matrix& operator=(const matrix& right); //deep copy
    int getColumns() const;
    int getRows() const;
};

#endif

マトリックス.cpp

#include "matrix.h"
#include <string>
#include <fstream>
#include <iostream>

void matrix::allocmem()
{
    b = new field*[r];

    for(int i=0; i < r; i++)
        b[i] = new field[c];
}

matrix::matrix(int rows,int columns) 
{
    c = columns;
    r = rows;

    allocmem();
}

matrix::matrix(std::string fName)
{
    read(fName);
}

matrix::~matrix()
{
    for(int i=0; i<r;i++)
        delete [] b[i];

    delete b;
}

void matrix::read(std::string fname) //load matrix from file
{

    std::ifstream is;

    is.open(fname);

    is>>r>>c; //get matrix dimensions
    allocmem();

    //go to the first row
    char dull = is.peek();
    while(dull != '\n'){dull = is.get();}


    for(int i=0;i<r;i++) 
    {   
        for(int j=0;j<c;j++)
        {   
            is>>b[i][j].value;
            b[i][j].row=i+1;
            b[i][j].column=j+1;
        }
        while(dull != '\n'){dull = is.get();}
    }   

    is.close();
}

matrix matrix::operator+(const matrix& right)
{
    matrix rMatrix(right.getRows(),right.getColumns());

    if((r != right.r) || (c != right.c))
    {   
        return NULL;
    }

    rMatrix.r = r;
    rMatrix.c = c;

    //matrix addition algorithm
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)    
        {   
            rMatrix.b[i][j].value = b[i][j].value+right.b[i][j].value;
            rMatrix.b[i][j].row = i+1;
            rMatrix.b[i][j].column = j+1;
        }

    return rMatrix;
}

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

    for(int i=0; i<r;i++)
        delete [] b[i];
    delete b;

    r = right.getRows();
    c = right.getColumns();

    allocmem();
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
        {
            b[i][j].value = right.b[i][j].value; //RUN-TIME ERROR!
            b[i][j].column = j+1;
            b[i][j].row = i+1;
        }

    return *this;
}

int matrix::getColumns() const
{
    return c;
}
int matrix::getRows() const
{
    return r;
}

午前:

4 4
10.5 20.7 30.5 40.1
0 0 15.4 9.8
4 2 -8.3 4.2
9.3 2.7 1.2 8.9

bmt:

4 4
-2.5 0.7 30.5 -54.1
0 1 0 9.8
4 7 8.3 4.2
7.3 2.7 -1.2 3.9

このプログラムは、ファイルから 2 つの行列を読み込み、それらの合計を計算する必要があります。実際には、ディープ コピー メソッド (operator ) 内でクラッシュし、次の=ようなエラーがスローされますここに画像の説明を入力
バグの場所と修正方法をお聞きしたいです。

4

2 に答える 2

1

C++ で動的メモリを処理する方法がわからないようです。

これは難しい問題なので、2 つの解決策があります。

  1. 生メモリを操作しない
  2. 生メモリの操作方法を学ぶ

明らかに、長期的には、生のメモリを操作する方法を学ぶ方が良いですが、短期的な解決策は次のとおりです。

std::vector<std::vector<field>> b;

さて、長期的に見てみましょう。

  1. 序文:学習目的以外で車輪を再発明しないでください
  2. 単一責任の原則に従う: クラスがリソースを管理するか、ビジネス機能を備えているかのいずれかであり、両方を備えているわけではない
  3. リソースを処理するクラスは、3 つの規則 (コピー コンストラクター、コピー代入演算子、およびデストラクターのすべてを慎重に記述する) に従う必要があり、これらのメソッド中に発生する例外の処理に特別な注意を払う必要があります。

じゃ、行こう!一般的なアーキテクチャは次のとおりです。

  • メモリを処理するリソース クラス (それだけです)
  • オペレーションを処理し、隠れたリソース クラスに依存するビジネス クラス

最初に少しかじります:

// No reason to have line and column there, is it ?
struct Field {
    Field(): value() {} // initialize value on construction, please!

    double value;
};

リソース クラスから始めます。

// An array of N*M "Field" elements
// It is minimalist, but minimalist is good!
class FieldsArray {
public:
    FieldsArray(size_t rows, size_t columns);
    FieldsArray(FieldsArray const& other);
    FieldsArray& operator=(FieldsArray const& other);
    ~FieldsArray();

    void swap(FieldsArray& other);

    Field& at(size_t i, size_t j);
    Field const& at(size_t i, size_t j) const;

private:
    void allocate(); // rows & columns must be set
    void release();  // rows & columns must be set

    size_t rows;
    size_t columns;
    Field** fields;
}; // class FieldsArray

inline void swap(FieldsArray& left, FieldsArray& right) {
    left.swap(right);
} // swap

定義に移ると、テーブルのテーブルを使用するのは不便であることがわかります (1 つの大きな N*M テーブルを使用する方が簡単だったでしょう)。

FieldsArray::FieldsArray(size_t rows, size_t columns):
    rows(rows), columns(columns)
{
    this->allocate();
} // FieldsArray::FieldsArray

FieldsArray::FieldsArray(FieldsArray const& other):
    rows(other.rows), columns(other.columns)
{
    // Perform deep copy
    this->allocate();

    try {
        for (size_t i = 0; i < rows; ++i) {
            for (size_t j = 0; j < columns; ++j) {
                fields[i][j] = other.fields[i][j];
            }
        }
    } catch(...) {
        this->release();
        throw; // rethrow
    }
} // FieldsArray::FieldsArray

FieldsArray& FieldsArray::operator=(FieldsArray const& other) {
    FieldsArray tmp(other);
    this->swap(tmp);
    return *tmp;
} // FieldsArray::operator=

FieldsArray::~FieldsArray() {
    this->release();
} // FieldsArray::~FieldsArray

void FieldsArray::swap(FieldsArray& other) {
    using std::swap;
    swap(this->rows, other.rows);
    swap(this->columns, other.columns);
    swap(this->fields, other.fields);
} // FieldsArray::swap

Field& FieldsArray::at(size_t i, size_t j) {
    assert(i < rows && "Wrong index!");
    assert(j < columns && "Wrong index!");

    return _fields[i][j];
} // FieldsArray::at

Field const& FieldsArray::at(size_t i, size_t j) const {
    assert(i < rows && "Wrong index!");
    assert(j < columns && "Wrong index!");

    return _fields[i][j];
} // FieldsArray::at

void FieldsArray::allocate(size_t rows, size_t columns) {
    fields = new Field*[rows];

    try {
        for (size_t i = 0; i < rows; ++i) { fields[i] = new Fields[columns]; }
    } catch(...) {
        this->release();
        throw; // rethrow
    }
} // FieldsArray::allocate

void FieldsArray::release() {
    for (size_t i = 0; i < rows; ++i) { delete[] fields[i]; }
    delete[] fields;
} // FieldsArray::release

std::vector<Field>ええ、それはすべて(サイズN * M)に相当するものを取得するためだけです。私はそれを台無しにしないことを願っています(注:C ++ 11では、std::unique_ptr<Field[]>大いに役立つでしょう...)

そしていよいよ、コア ビジネスに取り掛かります。

class Matrix {
public:
    Matrix(size_t rows, size_t columns): _data(rows, columns) {}

    Field& at(size_t i, size_t j) { return _data.at(i, j); }
    Field const& at(size_t i, size_t j) const { return _data.at(i, j); }

    Matrix& operator+=(Matrix const& other);

private:
    FieldsArray _data;
}; // class Matrix

inline Matrix operator+(Matrix const& left, Matrix const& right) {
    Matrix result(left);
    result += right;
    return result;
} // operator+

そして の定義operator+=:

Matrix& Matrix::operator+=(Matrix const& other) {
    assert(this->rows == other.rows && "Rows differ!");
    assert(this->columns == other.columns && "Columns differ!");

    for (size_t i = 0; i < rows; ++i) {
        for (size_t j = 0; j < columns; ++j) {
            this->at(i, j) += other.at(i, j);
        }
    }

    return *this;
} // Matrix::operator+=

他のメソッドの実装は、読者の演習として残しておきます ;)

于 2013-11-10T20:28:37.100 に答える