实验环境
操作系统:win10
gcc:8.1.0
开发软件vscode dev
实验内容
一、构造函数
CMatrix(): 不带参数的构造函数;
CMatrix(int nRow, int nCol, double *pData=NULL) : 带行、列及数据指针等参数的构造函数,并且参数带默认值;
CMatrix(const char * strPath): 带文件路径参数的构造函数;
CMatrix(const CMatrix& m): 拷贝构造函数
此外会用列表初始化成员变量:CMatrix(): m_nRow(0), m_nCol(0), m_pData(NULL);
bool Create(int nRow, int nCol, double *pData=NULL): 先删除原有空间,根据传入行列创建空间,如果pData不为空要将pData的内容拷贝到m_pData中。
二、析构函数
~CMatrix(): 调用Release();
Release(): 将内存释放,并将行列设置为0;
三、运算符重载
算术运算符重载:+, -, +=, -=
关系运算符重载:>, <, ==
下标操作符:[], ()
强制类型转换: double
赋值运算符:=,尤其注意当m1=m1特殊情况的处理
四、友元函数
输入和输出运输符:<<, >>
实验简述
本次实验中的CMatrix类的实质是一个三维的数组,实际上指代一个矩阵,其中第一和第二个维度依次为矩阵的长和宽,第三个维度的值为一个数组,即指代矩阵中依次序的值;而本次实验主要的目的是测试以CMatrix类为样例下不同的构造器和重载方法的实现。
CMatrix.h
#ifndef CMATRIX_H
#define CMATRIX_H
#include <iostream>
using namespace std;
class CMatrix
{
public:
// 构造器
CMatrix();
CMatrix(int nRow, int nCol, double *pData=NULL);
CMatrix(const CMatrix &m);
CMatrix(const char *strPath);
~CMatrix();
// 初始化对象方法
bool Create(int nRow, int nCol, double *pData=NULL);
// 释放内存方法
void Release();
// 设置指定对象方法
void Set(int nRow, int nCol, double dVale);
// 重载操作符
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);
CMatrix& operator-=(const CMatrix& m);
bool operator ==(const CMatrix& m);
bool operator !=(const CMatrix& m);
double & operator[](int nIndex);
double & operator()(int nRow, int nCol);
// 重载类型转换
operator double();
private:
int m_nRow;
int m_nCol;
double *m_pData = NULL;
};
// 重载运算符
CMatrix operator+(const CMatrix& m1, const CMatrix& m2);
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
关于头文件的部分,没有太多值得说的;需要注意的是
#ifndef CMATRIX_H
#define CMATRIX_H
....... //中略
#endif
用于防止被重负引用
using namespace std;
用于声明命名空间,所有调用了诸如这样的c++的库时,都需要声明命名空间;而std是C++标准程序库中的所有标识符被存放的一个namespace。
CMatrix.cpp
CMatrix::CMatrix():m_nRow(0),m_nCol(0),m_pData(0) //使用初始化表达式的无参构造器,以类的定义顺序传参
{
}
CMatrix::CMatrix(int nRow,int nCol,double*pData):m_pData(0) //有参构造器
{
Create(nRow, nCol, pData);
}
CMatrix::CMatrix(const CMatrix& m) : m_pData(0) //拷贝构造器
{
*this = m;
}
CMatrix::CMatrix(const char* strPath) //外部数据构造器
{
m_pData = 0;
m_nRow = m_nCol = 0;
ifstream cin(strPath);
cin >> *this;
}
以上的函数为对象的4种构造器,其分别的类别都有在上述的注释中说明,其中有参数的构造器调用的create函数会在下面给出。同时,在c++中,上述代码中出现的*this其实都是指代新构造的对象。
CMatrix::~CMatrix() //析构函数,在对象被调用完毕后自动执行
{
Release();
}
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;
}
create函数用于新建对象的方法,在有参构造器中有提到;剩下的两个函数则是配合作为析构函数,析构函数是指对象类在被建立后,一旦结束其生命周期,就会自动调用析构函数,而release会自动清空其使用的内存从而达到释放对象的目的
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;
}
CMatrix& CMatrix::operator=(const CMatrix& m) //赋值运算重载
{
if(this!=&m){
Create(m.m_nRow, m.m_nCol,m.m_pData);
}
return*this;
}
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& 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;
}
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+nCol<m_nRow*m_nCol);
return m_pData[nRow * m_nCol + nCol];
}
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;
}
bool CMatrix::operator !=(const CMatrix& m) //关系运算符重载
{
return!((*this) == m);
}
CMatrix::operator double() //类型转换重载
{
double ds = 0;
for (int i = 0;i < m_nRow * m_nCol;i++)
{
ds += m_pData[i];
}
return ds;
}
上述展示的是题目所要求的各项操作符,运算符等的重载;重载的概念是类的一个衍生概念,在java这些存在类的编程语言中同样存在,简单来说重载即是在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。其在c++的形式如上面展示的那样 由operator关键字+重构对象组成;需要注意的是,并非所有方法都可以重构,如析构函数就无法进行重构。
Main.cpp
话不多说,我们还是先把代码贴在下面
#include <iostream>
#include <stdio.h>
#include "CMatrix.h"
using namespace std;
int main(int argc, char** argv) {
double pData[9]={7,7,7,3,3,3};
CMatrix CMatrix1, CMatrix2(3,3,pData), CMatrix3("C:\\Users\\28968\\Desktop\\C++\\test.txt"), CMatrix4;
cout<<"please input the value of CMatrix1\n";
cin >> CMatrix1;
CMatrix2.Set(3,3,3737);
CMatrix4=CMatrix3;
CMatrix4[0] = 9999;
cout<<"CMatrix1:\n"<<CMatrix1<<"CMatrix2:\n"<<CMatrix2<<"CMatrix3:\n"<<CMatrix3<<"CMatrix4:\n"<<CMatrix4;
CMatrix4 += CMatrix3;
cout<<"CMatrix4 += CMatrix3:\n"<<CMatrix4;
return 0;
}
首先说明,main中调用新建了4个不同的CMatrix类,分别使用无参构造器;有参构造器;外部数据构造器和拷贝构造器。并且我们先为CMatrix1赋值,测试操作符的重载是否成功,并且将CMatrix4的值进行修改后依次输出来进行各项重载和构造器是否生效。最后抽样进行+=操作符的重载是否成功。
上图为test.txt的内容。
可以观察到实验结果如同预期。
小结
本次实验本预计使用vscode完成实验,但软件配置中出现一定的问题,导致软件无法读取工程性的c++软件,只能运行单独的c++代码;当运行工程性的代码时总会在应用头文件时提示无法找到类的方法的定义。