0

从方法返回对象后matrix matrix::operator+(const matrix& right),整个数组似乎被删除了!让我们在下面的简化示例中描述这个奇怪的问题:

主.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

b.mt:

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

这个程序应该从文件中加载两个矩阵,然后计算它们的总和。在实践中,它在深拷贝方法(操作符)内崩溃并=抛出这样的错误:在此处输入图像描述.
我想问你错误在哪里以及如何修复它。

4

2 回答 2

2

尝试添加一个复制构造函数。

当您从 operator+ 返回矩阵时,它使用复制构造函数来初始化结果。默认将逐个成员地复制调用(包括指向数据的指针)。然后你释放内存,因为当你离开 operator+ 时 rMatrix 被破坏,但你尝试在上面的上下文中为 operator = 访问它。

于 2013-11-10T19:36:12.513 回答
1

好吧,看来您不知道如何在 C++ 中处理动态内存。

这里没什么奇怪的,这是一个棘手的问题,所以你有 2 个解决方案:

  1. 不要操纵原始内存
  2. 学习如何操作原始内存

显然,从长远来看,学习如何操作原始内存更好……但短期解决方案是:

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

现在,让我们从长远来看。

  1. 前言:不要重复发明轮子,学习目的除外
  2. 遵守单一职责原则:要么一个类管理资源,要么它具有业务功能,但不能两者兼而有之
  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

在定义上,我们意识到使用表的表是不方便的(只使用一个大 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 回答