当前位置: 首页 > 工具软件 > CMatrix > 使用案例 >

实验1 CMatrix类设计与实现

慕河
2023-12-01

一、头文件的创建

C++对于创建一个类以及各项方法的推荐做法是在头文件.h中将类中所有的函数,变量预先定义,之后再在另一个.cpp文件中将这些所有方法一一实现,所以我们需要将我们对于矩阵类(我们之后都用CMatrix来表示这个类)的所有变量都在CMatrix.h中全部定义出来

#ifndef CMATRIX_H
#define CMATRIX_H
#include <iostream>
#include <string.h>
using namespace std;
class CMatrix
{
public:
    CMatrix();
    CMatrix(int nRow,int nCol,double *pData=NULL);
    CMatrix(const char * strPath);
    CMatrix(const CMatrix & m);
    ~CMatrix();
    bool Create(int nRow,int nCol,double *pData=NULL);
    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)
    {
        return m_pData[nRow*m_nCol+nCol];
    }

    bool operator ==(const CMatrix& m);
    bool operator !=(const CMatrix& m);

    operator double();

private:
    int m_nRow;
    int m_nCol;
    double * m_pData;
};
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

二、各类构造器的创建

接下来我们都是在CMatrix.cpp文件下实现这些方法,先引用一下头文件

#include "cmatrix.h"
#include <fstream>
#include <assert.h>
#include <string.h>

1.不带参数的构造器

CMatrix::CMatrix():m_nRow(0),m_nCol(0),m_pData(NULL)
{

}

2.带行、列及数据指针等参数的构造函数,并且参数带默认值;

CMatrix::CMatrix(int nRow,int nCol,double * pData):m_pData(NULL)
{
    Create(nRow,nCol,pData);
}
bool CMatrix::Create(int nRow,int nCol,double *pData)
{
    Release();
    m_pData = new double[nRow*nCol];
    m_nRow = nRow;
    m_nCol = nCol;
    if(pData)
    {
        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;
}

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);
    for(int i=0;i<m.m_nRow*m.m_nCol;i++)
    {
        is>>m.m_pData[i];
    }
    return is;
}

4.拷贝构造函数

通过将一个已经存在的类的所有值拷贝从而赋予给一个新的类

CMatrix::CMatrix(const CMatrix & m):m_pData(NULL)
{
    *this = m;
}

这里注意一件事,我们构造的这个拷贝函数和原有的变量共享一个地址,这只能算是浅拷贝,意思就是当我们修改其中一个变量的值,其他变量也会改变。

三、析构函数的创建

当一个类变量的生命周期到了尽头之后,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作,即释放内存,防止内存泄漏,当我们不写析构函数的时候,系统会自动替我们合成,但这样不确定性就影响着我们代码,所以一个完备的代码需要我们自己来完成析构函数的编写:

CMatrix::~CMatrix()
{
    Release();
}
void CMatrix::Release()
{
    if(m_pData)

    {
        delete []m_pData;
        m_pData = NULL;
    }
    m_nRow = m_nCol = 0;
}

四、运算符的重载

加减乘除四则运算,在小数整数中都可以自由使用,但对于我们自己动手创造的类,程序并不知道加减乘除如何计算(如在线性代数中矩阵的乘法有他自己的一套规则),所以也需要我们自己通过对运算符进行重载来使得我们的类也可以进行加减

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 m(m1);
    m += m2;
    return m;
}

2、关系运算符的重载

‘>’, ‘<’, '='运算符的重载

在定义大于小于符号的重定义之前,我们需要先定义一个值来表示变量的大小之分,这里我们使用矩阵所有值之和来表示他的大小:

CMatrix::operator double()
{
    double dSum=0;
    for(int i=0;i<m_nRow*m_nCol;i++)
    {
        dSum+=m_pData[i];
    }
    return dSum;
}
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;
}

3、下标操作符的重载

既然是矩阵,我们就需要访问某些特定位置参数的功能,于是我们还需要进行 “[ ]”,"( )"这些函数功能的重载

double & CMatrix::operator[](int nIndex)
{
    assert(nIndex<m_nRow*m_nCol);
    return m_pData[nIndex];
}
            
double & operator()(int nRow,int nCol)
{
    return m_pData[nRow*m_nCol+nCol];
}

4、赋值运算符的重载

CMatrix & CMatrix::operator=(const CMatrix & m)
{
    if(this==&m){
        return *this;
    }
        Create(m.m_nRow,m.m_nCol,m.m_pData);

    return *this;
}

五、输入和输出运输符:<<, >>的重载

就像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);
    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;
}

六、 运行测试代码

#include <iostream>
#include "ccomplex.h"
#include <stdio.h>
#include "cmatrix.h"
using namespace std;
int main(int argc, char *argv[])
{
    CMatrix m;
    double pData[10]={1,2,3,4,5,6};
    CMatrix m1(2,5,pData);
    {
        CMatrix m2(2,5);
    }
    CMatrix m3(m1);
    CMatrix ms[3] = {CMatrix(),CMatrix(2,5,pData),"d://1.tx"};
    CMatrix m4;
    m4 = m1;
    m1.Set(0,1,20);
    cout<<"m1=\n"<<m1;
    cout<<"m4=\n"<<m4;
    m4=m4;
    cout<<"after= m4=\n"<<m4;
    cout<<"m4[1]="<<m4[1];
    m4(0,1) = 4;
    m4+=m1;
    CMatrix m5;
    m5 = m4 + m1;

    double d = m5;
    cout<<"sum of m5="<<d;
    return 0;
}

实验结果:

m1=
2 5
1 20 3 4 5
6 0 0 0 0
m4=
2 5
1 2 3 4 5
6 0 0 0 0
after= m4=
2 5
1 2 3 4 5
6 0 0 0 0
m4[1]=2sum of m5=101

七、总结

通过本次自己手打一个完整的二维矩类,实现了各类构造器函数功能的编写,我学会了C++中面向对象编程中的各类代码书写规范,和public,friend,private,inline等各项修饰名词在C++中的各项作用。

但还是有很多不理解的地方,我会努力搞懂的!

 类似资料: