目录
🌟1【问题描述】
编写一个程序,定义一个安全、动态二维double型的数组类Matrix。
实现Matrix table(row,col)定义row行col列的二维数组, row和col为正整数;
实现table(i,j)访问table的第i行第j列的元素,行号和列号从0开始;
实现Matrix的输入输出(>>、<<);
实现矩阵加等、乘等运算(+=、*=),例:Matrix& operator+=(const Matrix&); Matrix& operator*=(const Matrix&);
实现矩阵的赋值运算(=),例:Matrix& operator=(const Matrix&)。
【输入形式】
第一行table1的行列值row1和col1,空格分隔;
第二行table1的初始化值,共row1*col1个数据,空格分隔;
第三行table2的行列值row2和col2,空格分隔;
第四行table2的初始化值,共row2*col2个数据,空格分隔;
【输出形式】
Matrix的输出格式为row行col列, 数据空格分隔;
若table1和table2不满足矩阵的加法和乘法运算规则,输出ERROR!;
依次输出以下表达式的值,每个输出间隔一行;
table1(row1/2,col1/2);
table1 *= table2;
table1 += table2;
table1 = table2。
💓2【问题分析】
解决这个问题需要问自己几个问题:
- 类实现数组的输入、输出及运算,需用到运算符重载?
- 动态数组用什么办法赋值呐?每次new,还是用顺序容器vector?
- 矩阵的加法、乘法、赋值实现的条件与逻辑?
2.1类的定义
先别着急,先来看看我定义了类里的哪些内容吧!
class Matrix
{
public:
//row:数组行数;col:数组列数;
//flag=1:可以输出;flag=0:出错,输出Error
int row, col, flag = 1;
//用vector嵌套来存放动态二维数组
vector<vector<double>> v;
//输出对应行列的元素
void table(const int i, const int j)
{
cout << v[i][j] << endl;
}
//我认为,由于采用嵌套vector,所以输入是难点
friend ostream& operator<<(ostream& os, Matrix &m);
friend istream& operator>>(istream& is, Matrix &m);
Matrix& operator+=(const Matrix&);
Matrix& operator*=(const Matrix&);
Matrix& operator=(const Matrix&);
};
- 成员属性:行数(row)、列数(col)、状态(flag,给输出服务,即判断经过矩阵运算后能不能输出,也就是矩阵的运算能否执行)。
- 成员函数:访问矩阵的某个元素(table)、输入输出重载、累加累乘重载、赋值重载。
2.2关于vector
这里,我选用了vector来实现动态数组。下图是C++的部分顺序容器,(源《C++ Primer》)实质就是把我们数据结构中自己写的顺序表、单链表、队列等封装好,直接供我们使用。在以下容器中,我们最常使用的就是vector,除非我们有其他更好的理由选择其他容器。
顺序容器几乎可以保存各种类型的元素,以vector为例
vector<int> v; v里面存放着int的数据类型,类比int v[N];
vector<double> v; v里面存放着double的数据类型,类比double v[N];
vector<string> v; v里面存放着string的数据类型,类比char[M][N];
以上其实就定义了三个动态数组,前者数组大小可变,后者类比C语言,数组大小需提前固定好。
那二维动态数组,也就是类似于矩阵呢?
vector<vector<double>> v; 本题所用数据类型,类比double v[M][N];
定义好了数据类型,那vector是怎么样进行操作的呢?它不能使用我们的直接给数组赋值的操作,比如v[i][j]=n;(把n赋值给v[i][j]),但却支持v[n]这种访问方法,(string、array、deque、string都支持),而包括四者在内的顺序容器,更应该使用迭代器(现在不明白也没关系)。
下图是顺序容器的一些插入操作(源《C++ Primer》),对于vector,我们最常用的就是第一个。
v.push_back(t); v是容器,t是添加的元素
vector其他的一些的操作:
v.pop_back(); 删除v中最后一个元素
v.clear(); 删除v中所有元素
2.3如何输入
有了以上储备,我们就可以开始输入数组啦!先看代码。
istream& operator>>(istream& is, Matrix& m)
{
//先输入行列数
cin >> m.row >> m.col;
//x为即将输入的数组元素
double x=0.0;
//v是二维数组:把一行里的元素作为整体存储,如:v[i][N]是数组的第i行
//vv是一维数组:存某一行里的不同列的元素,如:在存第一行元素时,vv[j]是第一行第j个元素
//vector作为顺序容器,长度可变,输入操作为v.push_back(x),不能用赋值的方法输入
vector<double> vv;
m.v.clear();
for (int i = 0; i < m.row; i++)
{
//清除vv里上一次存过的元素,方便多次使用
vv.clear();
for (int j = 0; j < m.col; j++)
{
cin >> x;
//用vv来存某一行的不同列的元素
vv.push_back(x) ;
}
//一行输入完后就整体存入v,即将此时的vv存入v
m.v.push_back(vv);
}
return is;
}
注意理解那两层循环的意义:vector支持一维数组的直接输入,那二维数组怎么输入呢?
vector创建一个一维数组并将其反复插入到二维数组中,如下图:
注意:为方便,我展示时看似数组大小固定了的,实际上是一边添加一个元素,一边分配一个空间。
2.4矩阵相加与相乘
矩阵加法:两矩阵行数列数相等,对应元素相加。
矩阵乘法:(m*n)第一个矩阵的列数n=(n*k)第二个矩阵的行数n,结果为(m*k)的矩阵,举个例子。
第一行第一列:
第一行第二列:
第二行第一列:
第二行第二列:
结果:
我们的算法也就应该模拟上述的过程。(上面的加号有点不对劲)
Matrix& Matrix:: operator+=(const Matrix& m)
{
//相加的条件:行列数均相等
//相加:即对应元素相加
if (row == m.row && col == m.col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
v[i][j] += m.v[i][j];
}
}
}
else
{
flag = 0;
}
return *this;
}
Matrix& Matrix:: operator*=(const Matrix& m)
{
//相乘条件:前一个的列数=后一个的行数
//注意两矩阵相乘的过程
if (col == m.row )
{
for (int i = 0; i < row; i++)
{
double sum = 0;
for (int j = 0; j < m.col; j++)
{
for (int k = 0; k < col; k++)
{
sum += v[i][k] * m.v[k][j];
}
v[i][j] = sum;
}
}
}
else
{
flag = 0;
}
return *this;
}
🌵3【完整代码】
#include<iostream>
#include<vector>
using namespace std;
class Matrix
{
public:
//row:数组行数;col:数组列数;
//flag=1:可以输出;flag=0:出错,输出Error
int row, col, flag = 1;
//用vector嵌套来存放动态二维数组
vector<vector<double>> v;
//输出对应行列的元素
void table(const int i, const int j)
{
cout << v[i][j] << endl;
}
//我认为,由于采用嵌套vector,所以输入是难点
friend ostream& operator<<(ostream& os, Matrix &m);
friend istream& operator>>(istream& is, Matrix &m);
Matrix& operator+=(const Matrix&);
Matrix& operator*=(const Matrix&);
Matrix& operator=(const Matrix&);
};
istream& operator>>(istream& is, Matrix& m)
{
//先输入行列数
cin >> m.row >> m.col;
//x为即将输入的数组元素
double x=0.0;
//v是二维数组:把一行里的元素作为整体存储,如:v[i][N]是数组的第i行
//vv是一维数组:存某一行里的不同列的元素,如:在存第一行元素时,vv[j]是第一行第j个元素
//vector作为顺序容器,长度可变,输入操作为v.push_back(x),不能用赋值的方法输入
vector<double> vv;
m.v.clear();
for (int i = 0; i < m.row; i++)
{
//清除vv里上一次存过的元素,方便多次使用
vv.clear();
for (int j = 0; j < m.col; j++)
{
cin >> x;
//用vv来存某一行的不同列的元素
vv.push_back(x) ;
}
//一行输入完后就整体存入v,即将此时的vv存入v
m.v.push_back(vv);
}
return is;
}
ostream& operator<<(ostream& os, Matrix& m)
{
if (m.flag != 0)
{
for (int i = 0; i < m.row - 1; i++)
{
for (int j = 0; j < m.col - 1; j++)
{
cout << m.v[i][j] << " ";
}
cout << m.v[i][m.col - 1] << endl;
}
//主要是考虑每一行最后一个元素后接endl,而不是" "
//最后一行的最后一个元素无endl
for (int j = 0; j < m.col - 1; j++)
{
cout << m.v[m.row - 1][j] << " ";
}
cout << m.v[m.row - 1][m.col - 1];
}
//如果被标志过,即两数组不能相加、相乘,就输出错误
else
{
cout << "ERROR!";
}
return os;
}
Matrix& Matrix:: operator+=(const Matrix& m)
{
//相加的条件:行列数均相等
//相加:即对应元素相加
if (row == m.row && col == m.col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
v[i][j] += m.v[i][j];
}
}
}
else
{
flag = 0;
}
return *this;
}
Matrix& Matrix:: operator*=(const Matrix& m)
{
//相乘条件:前一个的列数=后一个的行数
//注意两矩阵相乘的过程
if (col == m.row )
{
for (int i = 0; i < row; i++)
{
double sum = 0;
for (int j = 0; j < m.col; j++)
{
for (int k = 0; k < col; k++)
{
sum += v[i][k] * m.v[k][j];
}
v[i][j] = sum;
}
}
}
else
{
flag = 0;
}
return *this;
}
Matrix& Matrix::operator=(const Matrix& m)
{
//赋值类似于输入,需要修改row、col
flag = 1;
row = m.row;
col = m.col;
vector<double> vv;
v.clear();
for (int i = 0; i < m.row; i++)
{
vv.clear();
for (int j = 0; j < m.col; j++)
{
vv.push_back(m.v[i][j]);
}
v.push_back(vv);
}
return *this;
}
int main()
{
Matrix m1, m2;
cin >> m1 >> m2;
m1.table(m1.row / 2, m1.col / 2);
m1 *= m2;
cout << m1 << endl;
m1 += m2;
cout << m1 << endl;
m1 = m2;
cout << m1;
return 0;
}
补充:输入输出运算符重载可以格式化,感谢大家的建议!
头文件#include<iomanip>
std::ostream& operator<<(std::ostream& os, const Matrix &m);
std::istream& operator>>(std::istream& is, const Matrix &m);