鱼C论坛

 找回密码
 立即注册
查看: 1526|回复: 4

[已解决]关于析构函数和运算符重载问题

[复制链接]
发表于 2019-5-7 20:01:38 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
题目:定义两个矩阵对象,用运算符重载运算矩阵的加法和减法
我打了这样的代码,但是卡在了析构函数上面
  1. #include <iostream>
  2. using namespace std;
  3. class Matrix
  4. {
  5. private:
  6.         double* mat;
  7.         int x;//行
  8.         int y;//列
  9. public:
  10.         int Getx()
  11.         {
  12.                 return x;
  13.         }
  14.         int Gety()
  15.         {
  16.                 return y;
  17.         }
  18.         Matrix(int x, int y);
  19.         friend Matrix operator + (Matrix& m_a, Matrix& m_b);//矩阵加法;
  20.         friend Matrix operator - (Matrix m_a, Matrix m_b);//矩阵减法;
  21.         Matrix& operator = (const Matrix m);
  22.         void SetMatrix();//设置矩阵的大小
  23.         void Diapaly();
  24.         ~Matrix();
  25. };

  26. #include <iomanip>
  27. void Matrix::SetMatrix()
  28. {
  29.         for (int i = 0; i < x; i++)
  30.         {
  31.                 for (int j = 0; j < y; j++)
  32.                 {
  33.                         cout << i + 1 << "行," << j + 1 << "列 : ";
  34.                         cin >> *(mat + y * i + j);
  35.                 }
  36.                 cout << "\n";
  37.         }
  38. }
  39. Matrix::Matrix(int m, int n)
  40. {
  41.         x = m;
  42.         y = n;
  43.         mat = new double[x * y];
  44. }
  45. Matrix::~Matrix()
  46. {
  47.         if (mat)
  48.         {
  49.                 delete[]mat;//[b]如果把这个注释掉就可以正常运行[/b]
  50.         }
  51. }
  52. Matrix operator+(Matrix & m_a, Matrix & m_b)//加法
  53. {
  54.         Matrix ma(m_a.x, m_a.y);
  55.         for (int i = 0; i < m_a.x * ma.y; i++)
  56.         {
  57.                 ma.mat[i] = m_a.mat[i] + m_b.mat[i];
  58.         }
  59.         return ma;
  60. }
  61. Matrix operator-(Matrix m_a, Matrix m_b)//减法
  62. {
  63.         Matrix ma(m_a.x, m_a.y);
  64.         for (int i = 0; i < m_a.x * ma.y; i++)
  65.         {
  66.                 ma.mat[i] = m_a.mat[i] - m_b.mat[i];
  67.         }
  68.         return ma;
  69. }

  70. Matrix & Matrix::operator= (Matrix m)
  71. {
  72.         mat = new double[m.x * m.y];
  73.         for (int i = 0; i < m.x * m.y; i++)
  74.         {
  75.                 mat[i] = m.mat[i];
  76.         }
  77.         return *this;
  78. }
  79. void Matrix::Diapaly()
  80. {
  81.         for (int i = 0; i < x; i++)
  82.         {
  83.                 for (int j = 0; j < y; j++)
  84.                 {
  85.                         cout << setw(5) << *(mat + y * i + j) << " ";
  86.                 }
  87.                 cout << "\n";
  88.         }
  89. }
  90. int main()
  91. {
  92.         int m, n;
  93.         int flag;
  94.         cout << "A矩阵:" << endl;
  95.         cout << "请输入矩阵行数:";
  96.         cin >> m;
  97.         cout << "请输入矩阵列数:";
  98.         cin >> n;
  99.         Matrix A(m, n);
  100.         A.SetMatrix();
  101.         A.Diapaly();

  102.         cout << "=============\n";
  103.         cout << "B矩阵:" << endl;
  104.         cout << "请输入矩阵行数:";
  105.         cin >> m;
  106.         cout << "请输入矩阵列数:";
  107.         cin >> n;
  108.         Matrix B(m, n);
  109.         B.SetMatrix();
  110.         B.Diapaly();
  111.         cout << "==录入完毕!!==";
  112.         do
  113.         {
  114.                 int count = 0;
  115.                 cout << "请输入要进行的操作:" << endl;
  116.                 cout << "====【1】矩阵相加======" << endl;
  117.                 cout << "====【2】矩阵减法======" << endl;
  118.                 cout << "请输入相应的序号:" << endl;
  119.                 cin >> flag;
  120.                 switch (flag)
  121.                 {
  122.                 case 1:
  123.                 {
  124.                         if ((A.Getx() == B.Getx()) && (A.Gety() == B.Gety()))
  125.                         {
  126.                                 Matrix C(A.Getx(), B.Gety());
  127.                                 C = A + B;
  128.                                 cout << "结果: " << endl;
  129.                                 C.Diapaly();
  130.                         }
  131.                         else
  132.                         {
  133.                                 cout << "\n======行列不相等无法相加=====\n" << endl;
  134.                                 break;
  135.                         }
  136.                         break;
  137.                 }
  138.                 case 2:
  139.                 {
  140.                         if ((A.Getx() == B.Getx()) && (A.Gety() == B.Gety()))
  141.                         {
  142.                                 Matrix C(A.Getx(), B.Gety());
  143.                                 cout << "done" << endl;//测试
  144.                                 C = A - B;
  145.                                 cout << "结果: " << endl;
  146.                                 C.Diapaly();
  147.                         }
  148.                         else
  149.                         {
  150.                                 cout << "\n======行列不相等无法相减=====\n" << endl;
  151.                                 break;
  152.                         }
  153.                         break;
  154.                 }
  155.                 default:
  156.                         cout << "请输入正确的序号!!" << endl;
  157.                         break;
  158.                 }
  159.         } while (flag != 0);
  160.         return 0;
  161. }
复制代码

如果我把析构函数的释放内存那行注释掉就可以正常运行
但是不注释就会报错:
批注 2019-05-07 194846.png
问一下这样的错误是什么原因,
应该怎样才能让析构函数正常运行;
最佳答案
2019-5-7 21:50:46
楼主的问题是没有复制构造函数
没有复制构造函数那么默认的复制方案是浅拷贝,也就是拷贝前后的两个矩阵的mat是指向同一个内存块的,前一个析构时释放了这个内存块,后一个析构时重复释放就会导致错误

复制构造函数:
  1.         Matrix(const Matrix& m2):Matrix(m2.x,m2.y) {
  2.                 mat = new double[x*y];
  3.                 memcpy(mat, m2.mat, sizeof(double)*x*y);
  4.         }
复制代码


另外,你operator=的重载函数是会导致泄漏的,重新申请空间之前应该删除之前的空间:
  1. Matrix & Matrix::operator= (Matrix m)
  2. {
  3.         x = m.x;
  4.         y = m.y;
  5.         mat = (double*)realloc(mat, sizeof(double)*x*y);
  6.         for (int i = 0; i < m.x * m.y; i++)
  7.         {
  8.                 mat[i] = m.mat[i];
  9.         }
  10.         return *this;
  11. }
复制代码


然后,还有+ -的重载一般来说,型参应该是const Matrix&的,你这样的型参会导致额外的计算负担,

再提一点,作为c++,最好加上移动构造函数,能进一步减少无用功
  1.         Matrix(Matrix&& m2) :Matrix(m2.x, m2.y) {
  2.                 mat = m2.mat;
  3.                 m2.x = 0;
  4.                 m2.y = 0;
  5.                 m2.mat = nullptr;
  6.         }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2019-5-7 21:50:46 | 显示全部楼层    本楼为最佳答案   
楼主的问题是没有复制构造函数
没有复制构造函数那么默认的复制方案是浅拷贝,也就是拷贝前后的两个矩阵的mat是指向同一个内存块的,前一个析构时释放了这个内存块,后一个析构时重复释放就会导致错误

复制构造函数:
  1.         Matrix(const Matrix& m2):Matrix(m2.x,m2.y) {
  2.                 mat = new double[x*y];
  3.                 memcpy(mat, m2.mat, sizeof(double)*x*y);
  4.         }
复制代码


另外,你operator=的重载函数是会导致泄漏的,重新申请空间之前应该删除之前的空间:
  1. Matrix & Matrix::operator= (Matrix m)
  2. {
  3.         x = m.x;
  4.         y = m.y;
  5.         mat = (double*)realloc(mat, sizeof(double)*x*y);
  6.         for (int i = 0; i < m.x * m.y; i++)
  7.         {
  8.                 mat[i] = m.mat[i];
  9.         }
  10.         return *this;
  11. }
复制代码


然后,还有+ -的重载一般来说,型参应该是const Matrix&的,你这样的型参会导致额外的计算负担,

再提一点,作为c++,最好加上移动构造函数,能进一步减少无用功
  1.         Matrix(Matrix&& m2) :Matrix(m2.x, m2.y) {
  2.                 mat = m2.mat;
  3.                 m2.x = 0;
  4.                 m2.y = 0;
  5.                 m2.mat = nullptr;
  6.         }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-5-7 21:52:57 | 显示全部楼层
本帖最后由 Croper 于 2019-5-7 21:55 编辑

另外,正好我也写过矩阵类,分享一下,
我直接使用的模板管理长宽,内部就是个二维数组,这样就不需要复制构造函数和析构函数了
  1. //Matrix.h
  2. //矩阵类的实现
  3. //Created by Croper,lasted modifered at Apr.13.2019

  4. #ifndef _MATRIX_H_
  5. #define _MATRIX_H_

  6. #include <initializer_list>
  7. #include <iostream>


  8. template <int _cy, int _cx> class Matrix {
  9.         int _data[_cy][_cx];
  10. public:
  11. ///////////////////////////////////////////////////////////////////////////
  12. //                            函数声明
  13. ///////////////////////////////////////////////////////////////////////////

  14. //--------------------------构造函数----------------------------
  15.         //默认构造函数;
  16.         Matrix();
  17.         //使用初始化列表进行构造,直接一行一行输入,行间不需要使用分隔符;
  18.         Matrix(const std::initializer_list<int>&);
  19.         //复制构造函数
  20.         Matrix(const Matrix&) = default;

  21.        
  22. //-------------------==----运算符重载--------------------------
  23.         // 重载下标运算,以实现矩阵的双下标(a[i][j])写法
  24.         int* operator[](int rowindex);   
  25.         //const版本
  26.         const int* operator[](int rowindex) const;

  27.         //重载*运算,实现矩阵乘法
  28.         template <int _cz> Matrix<_cy, _cz> operator*(const Matrix<_cx, _cz>& m2);


  29.         //-------------------------其他函数------------------------------
  30.         // 矩阵的转置
  31.         Matrix<_cx, _cy> turn();

  32.         // 于控制台打印自身
  33.         void print();
  34. };

  35. ///////////////////////////////////////////////////////////////////////////
  36. //                            函数实现
  37. ///////////////////////////////////////////////////////////////////////////

  38. //默认构造函数;
  39. template<int _cy, int _cx>
  40. inline Matrix<_cy, _cx>::Matrix() {
  41.         memset(_data, 0, sizeof(_data));
  42. }

  43. //使用初始化列表进行构造,直接一行一行输入,行间不需要使用分隔符;
  44. template<int _cy, int _cx>
  45. inline Matrix<_cy, _cx>::Matrix(const std::initializer_list<int>& list) :Matrix() {
  46.         int *p = (int*)_data;
  47.         for (auto it = list.begin(); it != list.end(); ++it, ++p) {
  48.                 *p = *it;
  49.         }
  50. }

  51. // 重载下标运算,以实现矩阵的双下标(a[i][j])写法
  52. template<int _cy, int _cx>
  53. inline int* Matrix<_cy, _cx>::operator[](int rowindex) {
  54.         return _data[rowindex];
  55. }

  56. //const版本
  57. template<int _cy, int _cx>
  58. inline const int* Matrix<_cy, _cx>::operator[](int rowindex) const {
  59.         return const_cast<Matrix*>(this)->operator[](rowindex);
  60. }

  61. //重载*运算,实现矩阵乘法
  62. template<int _cy, int _cx>
  63. template <int _cz>
  64. Matrix<_cy, _cz> Matrix<_cy, _cx>::operator*(const Matrix<_cx, _cz>& m2) {
  65.         Matrix<_cy, _cz> ret;
  66.         for (int y = 0; y < _cy; ++y) for (int z = 0; z < _cz; ++z) {
  67.                 for (int x = 0; x < _cx; ++x) {
  68.                         ret[y][z] += _data[y][x] * m2[x][z];
  69.                 }
  70.         }
  71.         return ret;
  72. }

  73. // 矩阵的转置
  74. template<int _cy, int _cx>
  75. Matrix<_cx, _cy> Matrix<_cy, _cx>::turn() {
  76.         Matrix<_cx, _cy> ret;
  77.         for (int i = 0; i < _cx; ++i) {
  78.                 for (int j = 0; j < _cy; ++j) {
  79.                         ret[i][j] = _data[j][i];
  80.                 }
  81.         }
  82.         return ret;
  83. }

  84. // 打印自身
  85. template<int _cy, int _cx>
  86. void Matrix<_cy, _cx>::print() {
  87.         std::cout << std::endl;
  88.         for (int y = 0; y < _cy; ++y) {
  89.                 for (int x = 0; x < _cx; ++x) {
  90.                         std::cout << '\t' << _data[y][x];
  91.                 }
  92.                 std::cout << std::endl;
  93.         }
  94. }

  95. #endif
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-5-8 07:28:03 | 显示全部楼层
感谢
不过我还有一些不懂:
我只进行了运算符重载,为什么会调用拷贝构造函数?
我确定是运行到重载时出现的问题
是不是在这个函数中我定义了一个新的对象从而调用拷贝构造函数的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-5-8 14:03:13 | 显示全部楼层
本帖最后由 Croper 于 2019-5-8 14:07 编辑
  1. C = A + B;
复制代码

我们来分析一下这一句代码发生了什么
首先,A和B作为实参,调用operator+
  1. Matrix operator+(Matrix & m_a, Matrix & m_b)//加法
  2. {
  3.         Matrix ma(m_a.x, m_a.y);
  4.         for (int i = 0; i < m_a.x * ma.y; i++)
  5.         {
  6.                 ma.mat[i] = m_a.mat[i] + m_b.mat[i];
  7.         }
  8.         return ma;
  9. }
复制代码


(顺便一提,这里A和B传递的引用,不会调用复制构造函数。但是你的operator-的参数形式直接是Matrix,那么函数里的形参是A,B的复制,是调用了复制构造函数的)
在函数内部,新建了一个局部变量ma

函数结束,返回ma,
到这时,都没有调用复制构造函数

接下来就要注意了,
首先,系统会产生一个临时的Matrix变量(后面就叫它_tmp吧),调用复制构造函数,复制ma。
然后operator+函数结束,释放空间,ma作为局部变量同时也被析构

然后执行C=tmp,调用operator=(其实准确地说应该是调用的operator=(Matrix&&),移动赋值函数,因为这里的tmp是将亡值,愿意学习的话可以去搜索一下关于左值,右值,纯右值,将亡值的定义,但是没有这个函数,系统就拿operator=(Matrix)代替了)
tmp再次作为实参传入,而你的型参是非引用类型的,因此会再次执行复制构造函数,生成局部变量m,
然后函数结束,C被拷贝,析构m,

最后语句结束,析构tmp

所以在这个语句中,如果你没有复制构造函数,那么同一块内存同时被ma,tmp和m指向,当然会报错。实际上,在析构m的时候就已经报错了,根本轮不到析构tmp

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-7-1 23:00

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表