C++学习01:如何使用C++创建二维矩阵类并实现各类运算符的重载
1.头文件的创建
C++对于创建一个类以及各项方法的推荐做法是在头文件.h中将类中所有的函数,变量预先定义,之后再在另一个.cpp文件中将这些所有方法一一实现,所以我们需要将我们对于矩阵类(我们之后都用CMatrix来表示这个类)的所有变量都在CMatrix.h中全部定义出来
#ifndef CMATRIX_H
#define CMATRIX_H
#include<iostream>
using namespace std;
class CMatrix
{
private:
int m_nRow;
int m_nCol;
double * m_pData=NULL;
public:
CMatrix();
CMatrix(int nRow, int nCol, double * pDate = NULL);
CMatrix(const CMatrix& m);
CMatrix(const char * strPah);
~CMatrix();
bool Create(int nRow, int nCol, double* pDate);
void Set(int nRow, int nCol, double dVale);
void Release();
friend istream & operator >>(istream& is, CMatrix & m);
friend ostream& operator <<(ostream& os, const CMatrix &m);
CMatrix& operator = (const CMatrix& m);
CMatrix& operator+=(const CMatrix& m);
double & operator[](int nIndex);
double & operator()(int nRow,int nCol);
bool operator == (const CMatrix& m);
bool operator != (const CMatrix& m);
operator double();
};
CMatrix operator+(const CMatrix& m1, const CMatrix& m2);
inline void CMatrix::Set(int nRow, int nCol, double dVal){
m_pData[nRow*m_nCol + nCol]=dVal;
}
#endif
在头文件创造完之后,我们就开始对其中所有预先定义的方法进行一一实现,首先我们先对构造器进行实现
2.各类构造器的创建
接下来我们都是在CMatrix.cpp文件下实现这些方法,先引用一下头文件
#include "cmatrix.h"
#include <fstream>
#include <assert.h>
#include<iostream>
using namespace std;
2.1 不带参数的构造器
CMatrix::CMatrix():m_nRow(0),m_nCol(0),m_pData(NULL)
{
}
CMatrix::CMatrix():m_nRow(0),m_pData(NULL)
{
m_nCol = 0
}
CMatrix::CMatrix()
{
m_nCol = 0;
m_nRow = 0;
m_pData = NULL;
}
2.2 带行、列及数据指针等参数的构造函数,并且参数带默认值;
CMatrix::CMatrix(int nRow, int nCol, double * pData=NULL){
m_pData=NULL;
Create(nRow, nCol, pData);
}
bool CMatrix::Create(int nRow, int nCol, double *pData){
Release();
m_nRow = nRow;
m_nCol = nCol;
m_pData=new double[nRow*nCol];
if(m_pData!=NULL){
if(pData!=NULL){
memcpy(m_pData, pData, nRow*nCol*sizeof (double));
}
}
return true;
}
void CMatrix::Release()
{
if(m_pData)
{
delete []m_pData;
m_pData = NULL;
}
m_nRow = m_nCol=0;
}
2.3 带文件路径参数的构造函数;
在许多大规模对类的操作中,我们需要使用文件输入用来代替繁杂的手动输入数值,所以创造从文件输入来创造类变量对于类的完整性也是非常重要的:
CMatrix::CMatrix(const char * strPath)
{
m_pData = NULL;
m_nRow = m_nCol =0;
ifstream cin(strPath);
cin>>*this;
}
但是这里注意普通的 ‘>>’ 运算符只能传入一些整形数,浮点数,字符串,他是不知道如何读取我们创建的这个类,所以我们还需要对该运算符进行重载:
friend istream & operator >>(istream& is, CMatrix & m);
istream & operator>>(istream& is, CMatrix & m)
{
is>>m.m_nRow>>m.m_nCol;
m.Create(m.m_nRow, m.m_nCol,0);
for(int i=0;i<m.m_nRow*m.m_nCol;i++)
{
is>>m.m_pData[i];
}
return is;
}
这里还需要注意一件事,就是我们从文件创造的变量,我们的文件必须符合格式,如先给出行数,列数,在一一写入矩阵的值(示例如下):
2 2
1
2
3
4
2.4 拷贝构造函数
通过将一个已经存在的类的所有值拷贝从而赋予给一个新的类
CMatrix::CMatrix(const CMatrix& m):m_pData(NULL)
{
*this = m;
}
这里注意一件事,我们构造的这个拷贝函数和原有的变量共享一个地址,这只能算是浅拷贝,意思就是当我们修改其中一个变量的值,其他变量也会改变。
3.析构函数的创建
当一个类变量的生命周期到了尽头之后,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作,即释放内存,防止内存泄漏,当我们不写析构函数的时候,系统会自动替我们合成,但这样不确定性就影响着我们代码,所以一个完备的代码需要我们自己来完成析构函数的编写:
CMatrix::~CMatrix()
{
Release();
}
void CMatrix::Release()
{
if(m_pData)
{
delete []m_pData;
m_pData = NULL;
}
m_nRow = m_nCol=0;
}
4. 运算符的重载
加减乘除四则运算,在小数整数中都可以自由使用,但对于我们自己动手创造的类,程序并不知道加减乘除如何计算(如在线性代数中矩阵的乘法有他自己的一套规则),所以也需要我们自己通过对运算符进行重载来使得我们的类也可以进行加减
4.1 “ + ”, “+=” 号的重载
CMatrix& CMatrix::operator+=(const CMatrix &m)
{
assert(m_nRow==m.m_nRow && m_nCol == m.m_nCol);
for (int i=0; i<m_nRow*m_nCol;i++)
{
m_pData[i]+=m.m_pData[i];
}
return *this;
}
CMatrix operator+(const CMatrix& m1, const CMatrix& m2)
{
CMatrix m3(m1);
m3+=m2;
return m3;
}
4.2 " - ", " -= "的重载
减法不过是加法的逆运算,只需将上面的函数稍作修改即可:
CMatrix& CMatrix::operator-=(const CMatrix &m)
{
assert(m_nRow==m.m_nRow && m_nCol == m.m_nCol);
for (int i=0; i<m_nRow*m_nCol;i++)
{
m_pData[i]-=m.m_pData[i];
}
return *this;
}
CMatrix operator-(const CMatrix& m1, const CMatrix& m2)
{
CMatrix m3(m1);
m3-=m2;
return m3;
}
4.3 关系运算符的重载
‘>’, ‘<’, '='运算符的重载
在定义大于小于符号的重定义之前,我们需要先定义一个值来表示变量的大小之分,这里我们使用矩阵所有值之和来表示他的大小:
CMatrix::operator double()
{
double dS=0;
for(int i=0;i<m_nRow*m_nCol;i++)
{
dS+=m_pData[i];
}
return dS;
}
bool operator > (CMatrix& m1, CMatrix& m2)
{
return double(m1)>double(m2);
}
bool operator < (CMatrix& m1, CMatrix& m2)
{
return double(m1)<double(m2);
}
bool CMatrix::operator==(const CMatrix &m)
{
if(!(m_nRow==m.m_nRow && m_nCol == m.m_nCol))
{
return false;
}
for(int i=0;i<m_nRow*m_nCol;i++)
{
if(m_pData[i]!=m.m_pData[i])
{
return false;
}
}
return true;
}
4.4.下标操作符的重载
既然是矩阵,我们就需要访问某些特定位置参数的功能,于是我们还需要进行 “[ ]”,"( )"这些函数功能的重载
double & CMatrix::operator[](int nIndex)
{
assert(nIndex<m_nRow*m_nCol);
return m_pData[nIndex];
}
double & CMatrix::operator()(int nRow, int nCol)
{
assert(nRow*m_nCol+nCol < m_nRow*m_nCol);
return m_pData[nRow*m_nCol + nCol];
}
4.5 赋值运算符的重载
CMatrix& CMatrix::operator=(const CMatrix &m)
{
if(this!=&m){
Create(m.m_nRow, m.m_nCol, m.m_pData);
}
return *this;
}
5.输入和输出运输符:<<, >>的重载
就像java对每个类的toStrinfg()方法一样,我们同样需要定义类的如何向外输入和输出同时,由于需要访问私有变量,我们需要在定义函数的时候将他定义为友元函数
friend istream & operator >>(istream& is, CMatrix & m);
friend ostream& operator <<(ostream& os, const CMatrix &m);
istream & operator>>(istream& is, CMatrix & m)
{
is>>m.m_nRow>>m.m_nCol;
m.Create(m.m_nRow, m.m_nCol,0);
for(int i=0;i<m.m_nRow*m.m_nCol;i++)
{
is>>m.m_pData[i];
}
return is;
}
ostream & operator<<(ostream & os, const CMatrix &m)
{
os<<m.m_nRow<<" "<<m.m_nCol<<endl;
double *pData = m.m_pData;
for(int i=0;i<m.m_nRow;i++)
{
for(int j=0;j<m.m_nCol;j++)
{
os<<*pData++<<" ";
}
os<<endl;
}
return os;
}
6. 运行测试代码
编写完上述这么多方法,我们当然需要测试一下代码来保证各个代码的正确性,编写测试代码如下:
#include <iostream>
#include "ccomplex.h"
#include<stdio.h>
#include "cmatrix.h"
using namespace std;
int main()
{
double pData[10] = {2, 3, 4,5 };
CMatrix m1,m2(2,5,pData),m3("D:\\1.txt"), m4(m2);
cin>>m1;
m2.Set(1,3,10);
cout<<m1<<m2<<m3<<m4;
m4=m3;
m4[1]=m4+1;
if(m4==m3)
{
cout<<"Error!!!!!"<<endl;
}
m4-=m3;
cout<<"sum of m4 = "<<double(m4)<<endl;
bool result = (m4>m3);
cout<<"m4 > m3 the Result is "<<result<<endl;
return 0;
}
实验结果:
6.结语
通过本次自己手打一个完整的二维矩类,实现了各类构造器函数功能的编写,我学会了C++中面向对象编程中的各类代码书写规范,和public,friend,private,inline等各项修饰名词在C++中的各项作用。以及学会了各项操作符的重载,了解了C++相对于java语言面向对象编程的更自由,拘束少,所以在细节方面也需更加注意。
|