刚开始学习C++,有什么地方写的不对的,恳请大家批评指正!
要求:定义矩阵类,实现矩阵相乘,并将结果输出在屏幕上、保存到 .txt文件中
分析问题:定义一个矩阵类Class my_matrix
,使用指针成员变量elem
实现大小可变的矩阵(此处用一维数组存放矩阵的元素),定义私有成员row、col
。必备的是构造函数、复制构造函数、析构函数;为了实现深拷贝,需要重载运算符=
;由于原有的乘号*并不知道如何对你的矩阵类进行操作,所以要重载运算符*
;在屏幕上输出数组,需要重载流运算符<<
;矩阵元素输入时需要函数void getelem()
获取元素,结果保存到.txt文件中需要一个成员函数void save_matrix()
。
功能:对象初始化(此时对象已经分配了存储空间)。
必要性:1. 默认的构造函数实际上不做任何操作 2. 对象没有初始化就使用,会导致程序出错。
my_matrix(int r =0, int c =0) :row(r), col(c) //构造函数!!
{
elem = new long double[row*col]();
//加一个括号就全部初始化成0了
// elem = new long double[row*col];
/* for (int i = 0; i < row*col; i++) {
elem[i] = 0.0;
}
cout << "构造函数被调用" << endl;*/
}
什么情况下起作用:
1. 用一个对象初始化同类的另一个对象——`my_matrix M1(M2)`
2. 某函数的形参是类的对象(也就是按值传递,如果是对象的引用传递则不会调用)
3. 函数的返回值是类的对象或引用(因为进入函数内部的是一个临时值,离开函数就全部消失了)
my_matrix(const my_matrix &M) //复制构造函数
{
row = M.row;
col = M.col;
elem = new long double[M.row*M.col];
for (int i = 0; i < row*col; i++) //所有的数都复制一遍
{
elem[i] = M.elem[i];
}
cout << "复制构造函数被调用" << endl;
}
在这里学到一个新的函数void *memcpy(void *dest, void *src, unsigned int count)
,可以实现一个字节一个字节的拷贝。但是要注意几个问题:1.首先要判断指针变量elem是不是空的,直接return。 2.在编写中发现,如果没有elem = new long double[M.row*M.col];
,则会提示 0xC0000005: 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突。我的想法是如果不加上这句话,memcpy函数不知道该复制到哪里吧。
所以改过之后可以这样写。
my_matrix(const my_matrix &M) //复制构造函数
{
if (!M.elem) {
elem = NULL;
col = 0; row = 0;
return *this;}
col = M.col;
row = M.row;
elem = new long double[M.row*M.col];
memcpy(elem, M.elem, sizeof(long double)*col*row);
}
作用:释放分配的空间。
构造函数可以有多个,但是析构函数只能有一个。
~my_matrix(void) //析构函数
{
delete[] elem;
cout << "析构函数被调用" << endl;
}
作用:扩展运算符的运算范围,可以应用于类所表示的数据类型。
浅拷贝:将原来的指针复制一遍(并没有给新的变量提供新的存储空间)。新的和旧的指针指向同一个内存。如果原来变量的内存释放(析构),那么复制的那个变量的指针就会变成野指针。并且最后析构的时候,将一块内存析构两次,也会报错。
深拷贝:申请新的内存空间,使得两个变量指向不同的内存空间。
在这里也可以用到memcpy(elem, M.elem, sizeof(long double)*col*row);
my_matrix& operator=(const my_matrix& M) //深拷贝
{
col = M.col;
row = M.row;
elem = new long double[row*col];
for (int i = 0; i < row*col; i++)
elem[i] = M.elem[i];
return *this;
}
将乘号*重载为类的友元函数或者成员函数都可以,重载为成员函数需要使用this指针。
注意由于最后是return temp
,所以在不能直接把temp的维度初始化为temp(matrix1.row,matrix2.col)
,不然在最后检验的行列数目不匹配的时候,会输出一个全是0的矩阵,而不是一个空的矩阵。
friend my_matrix operator*(const my_matrix& matrix1, const my_matrix& matrix2) {
my_matrix temp;
cout << "*运算符重载" << endl;
if (matrix1.col == matrix2.row) {
temp.row = matrix1.row;
temp.col = matrix2.col;
temp.elem = new long double [temp.row*temp.col]();
for (int i = 0; i < matrix1.row; i++) {
for (int j = 0; j < matrix2.col; j++) {
for (int k = 0; k < matrix1.col; k++) {
temp.elem[i*matrix2.col + j] += matrix1.elem[i*matrix1.col + k] * matrix2.elem[k*matrix2.col + j];
}
}
}
}
else {
cout << "行数和列数不等" << endl;
}
return temp;
}
流运算符必须是全局函数的形式,不能是成员函数。cout是iostream中定义的,是ostream类的对象。
返回值是ostream的引用,这样的话就可以连续输出。第一个参数要是cout
,因为使用的时候调用函数为cout.operator<<(),第二个参数是类的对象。
这个函数的写法很奇妙啊,第一次把另一个类的对象写到函数里面。
friend ostream&operator<<(ostream &out, const my_matrix& matrix)
{
for (int i = 0; i < matrix.row; i++) {
for (int j = 0; j < matrix.col; j++) {
out << matrix.elem[i*matrix.col + j] << " ";
}
out << endl;
}
return out;
}
void getelem() //获取元素
{
cout << "输入元素" << endl;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
cin >> elem[i*col + j];
}
}
}
void save_matrix()
{
ofstream outfile("matrix_nn.txt");
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
outfile << elem[i] << " ";
}
outfile << endl;
}
}