删除动态表时c ++分段错误

时间:2013-04-18 13:49:04

标签: c++ class pointers constructor

每个人我都要创建一个动态矩阵,这里是我的构造函数和析构函数:

Board::Board() {
    a_l=0;
    a_c=0;
    a_mp=NULL;
}
Board::Board(const Board&t) {
    a_l=t.a_l;
    a_c=t.a_c;
    a_mp=t.a_mp;
    Memory();
    copy(t);
}
Board::Board(int nl, int nc) {
    a_l=nl;
    a_c=nc;
    Memory();
}
Board::~Board() {
    freeMemory();
}

// PRIVATE METHODS

void Board::copy(const Board &t) {
    int a_l, a_c;
    int ** a_mp;
    a_l=t.a_l;
    a_c=t.a_c;
    for(int i=a_l;i<a_c;i++) {
        for(int j=a_c;j<a_l;j++) {
            a_mp[i][j]=t.a_mp[i][j];
        }
    }
}
void Board::freeMemory() {
    for(int i=0;i<a_l-1;i++) {
        delete [] a_mp[i];
    }
    delete [] a_mp;
}
void Board::Memory() {
    char ** a_mp;
    a_mp = new char*[a_l];
    for(int i =0;i<a_l; i++) {
        a_mp[i]=new char[a_c];
        for(int j=0;j<a_c;j++)
            a_mp[i][j]='-';
    }
}

Board是类,a_l和a_c是矩阵的行数和列数。在我的主要部分,我宣布一个Board变量,然后我这样做:

board=Board(5,5);

它编译,但是当我想显示它时,例如:

cout << board.Cols() << endl;

这是方法:

int Board::Cols() const {
    return (a_c);
}

它显示0.好像它没有用我说的参数创建板。 当我执行此操作board=Board(5,5);时程序崩溃,所以我使用调试器,它说它停在删除的这一行:

board=Board(5,5);

我不知道为什么它崩溃了,我不知道为什么它没有保留我声明的板变量的值! 谁知道为什么?

编辑:rMemory =记忆,这是一个来自这里的类型而不是来自程序

4 个答案:

答案 0 :(得分:3)

int ** a_mp;

需要a_mp。否则您要声明一个新变量。而不是使用成员。

然后需要分配给它。现在不是。


void Board::copy(const Board &t) {
    int a_l, a_c;
    a_mp = new char[t.a_l][t.a_c];
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // a_l=t.a_l; These have already be done in the copy constructor. 
    // a_c=t.a_c;
    for(int i=a_l;i<a_c;i++) {
        for(int j=a_c;j<a_l;j++) {
            a_mp[i][j]=t.a_mp[i][j];
        }
    }

}

或者如果您愿意,可以在复制构造函数中分配它

Board::Board(const Board&t) {
    a_l=t.a_l;
    a_c=t.a_c;
    // a_mp=t.a_mp;  // This is wrong!!
    a_mp = new char[t.a_l][t.a_c];
    copy(t);
}

答案 1 :(得分:2)

首先,这个作业

board=Board(5,5);

过于复杂。你可以宣布

Board board(5,5);

直接(额外的工作可能会被优化,但它不是惯用的。)


其次,无论你在哪里做类似的事情:

void Board::copy(const Board &t) {
    int a_l, a_c;
    int ** a_mp;

你是阴影对象的成员变量。也就是说,a_l现在引用此函数内的局部整数变量,而不是对象成员。如果要查看或更改对象,则必须引用this->a_l


现在,复制构造函数

Board::Board(const Board&t) {
    a_l=t.a_l;
    a_c=t.a_c;
    a_mp=t.a_mp;
    Memory();
    copy(t);
}

确实

  1. 副本(它与您正在复制的对象共享a_mp指针)
  2. 调用Memory,它分配新的内存块,仅在a_mp内部称为Memory,然后泄露(当该局部变量熄灭时)范围)
  3. 调用copy,它将原始对象值复制到一个名为a_mp未初始化的本地指针中(这是未定义的行为,因为这些写操作可以去任何地方)
  4. a_mp首先选择的值结束,因此现在两个 Board个实例具有相同的a_mp值,并且它将被释放两次

  5. 下面是一些修复这些问题的示例代码:我已经尽可能少地更改了代码的结构(为了便于阅读,我们重命名了一些代码)。还有很大的改进空间,但至少应该是正确的。

    class Board {
        int rows;
        int cols;
        char **data;
    public:
        Board(): rows(0), cols(0), data(0) {}
        Board(int nl, int nc) : rows(nl), cols(nc) 
        {
            allocate_data();
        }
        Board(const Board& other)
            : rows(other.rows), cols(other.cols)
        {
            allocate_data();
            copy_data(other);
        }
        ~Board() {
            free_data();
        }
    private:
        void copy_data(const Board &other) {
            for(int r=0; r<rows; r++)
                for(int c=0; c<cols; c++)
                    data[r][c]=t.data[r][c];
        }
        void free_data() {
            for(int r=0; r<rows; r++)
                delete [] data[r];
            delete [] data;
        }
        void allocate_data() {
            data = new char*[rows];
            for(int r=0; r<rows; r++) {
                data[r]=new char[cols];
                for(int c=0; c<cols; c++)
                    data[r][c]='-';
            }
        }
    };
    

    请注意,如果使用复制构造函数,则此方法可以正常工作,但默认生成的赋值运算符仍然是错误的。正如丹尼尔·韦伯在评论中指出的那样,rule of three表明你也应该写这个:

        Board& operator=(const Board& other) {
            free_data();
            rows = other.rows;
            cols = other.cols;
            allocate_data();
            copy_data(other);
        }
    

    请注意,复制赋值运算符需要处理已初始化的目标对象,并且可能没有正确的尺寸。如果新的(other)电路板较大,您可以将其改进为仅重新分配。

    如果您有C ++ 11支持,您还可以添加复制构造函数和赋值运算符的 move 等效项:

        Board(Board&& original)
            : rows(original.rows), cols(original.cols)
        {
            data = original.data;
            original.data = NULL;
        }
        Board& operator=(Board&& original) {
            free_data();
            rows = original.rows;
            cols = original.cols;
            data = original.data;
            original.data = NULL;
        }
    

答案 2 :(得分:1)

for(int i=0;i<a_l-1;i++)中的

freeMemoryi=0i=3,这是&lt; 5-1。 由于您的分配循环运行for(int i =0;i<a_l; i++),因此缺少一行,因此从i=0运行到i=4。您再分配一行。

另一件事是:copy()做什么?将t.a_l和t.a_c的值复制到临时变量,一旦复制结束就删除,并将值分配给未分配的内存(临时** a_mp)。 删除此函数中的声明和赋值,只保留a_mp数据副本。

void Board::copy(Board const &t) {
    for(int i=a_l;i<a_c;i++) {
        for(int j=a_c;j<a_l;j++) {
            a_mp[i][j]=t.a_mp[i][j];
        }
    }
}

我做了什么:

  • 删除Memory()
  • 中的a_mp声明
  • 添加作业运算符 - &gt; What is The Rule of Three?
  • 检查freeMemory()函数中的NULL指针

看起来像这样:

class Board
{
public:
  int a_l, a_c;
  char ** a_mp;

  Board() : a_l(0), a_c(0), a_mp(NULL) 
  {
  }
  Board(const Board&t) : a_l(t.a_l), a_c(t.a_c), a_mp(NULL) 
  {
    Memory();
    copy(t);
  }
  Board(int nl, int nc) : a_l(nl), a_c(nc), a_mp(NULL) 
  {
    Memory();
  }

  Board& operator= (Board const &t)
  {
    freeMemory();
    a_l = t.a_l;
    a_c = t.a_c;
    Memory();
    copy(t);
    return *this;
  }

  Board::~Board() 
  {
    freeMemory();
  }

  // PRIVATE METHODS

  void copy(const Board &t) 
  {
    for(int i=a_l;i<a_c;i++) 
    {
      for(int j=a_c;j<a_l;j++) 
      {
        a_mp[i][j]=t.a_mp[i][j];
      }
    }
  }
  void freeMemory() 
  {
    if (a_mp == NULL)
    {
      for(int i=0;i<a_l;i++) 
      {
        delete [] a_mp[i];
      }
      delete [] a_mp;
    }
  }
  void Memory() {
    a_mp = new char*[a_l];
    for(int i =0;i<a_l; i++) 
    {
      a_mp[i]=new char[a_c];
      for(int j=0;j<a_c;j++) a_mp[i][j] = '-';
    }
  }

  int Cols() const 
  {
    return (a_c);
  }

};

作品。

Board testboard;
testboard = Board(5,5);
cout << "Cols are: " << testboard.Cols() << endl;

打印:“Cols是:5”。

答案 3 :(得分:1)

void Board::Memory() {
    char ** a_mp;
    a_mp = new char*[a_l];
    for(int i =0;i<a_l; i++) {
        a_mp[i]=new char[a_c];
        for(int j=0;j<a_c;j++)
            a_mp[i][j]='-';
    }
}

您在堆栈上声明了一个名为a_mp的局部变量。该指针指向堆上所有已分配的内存。然后它在Memory()调用结束时超出范围。现在您无法访问刚分配的任何内存。这真是难过;这真是伤心。 a_mp应该是一个成员变量,因此在内存完成后你仍然可以引用你的数据。因此,你的析构函数知道要释放什么内存。

即。删除此行:char ** a_mp;