文章目录
初识C++:CMatrix类的实现
1.头文件声明 Cmatrix.h
头文件主要用于声明CMatrix
类以及类内部的属性和构造函数,和各种实现方法。
#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();
// vscode
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
2.类内部方法,函数的实现 CMatrix.cpp
2.1构造器(Constructor)
构造器最大的用处就是在创建对象时执行初始化,当创建一个对象时,系统会为这个对象的实例进行默认的初始化。如果想改变这种默认的初始化,就可以通过自定义构造器来实现。
由于c++面向对象语言的特性,构造器可以实现重载,即一个类可以有多个构造器。一个类的构造器的名称必须与该类的名称一致。
2.1.1 缺省构造器
// 无参构造器:
CMatrix::CMatrix()
{
m_nRow = 0;
m_nCol = 0;
m_pData = NULL;
}
无参构造器(使用初始化表达式):
// 无参构造器(使用初始化表达式)
// 其中传入参数的顺序和在类间定义时的顺序一致
// 在初始化时更有效率
CMatrix::CMatrix():m_nRow(0),m_nCol(0),m_pData(0)
{
//初始化为NULL
}
2.1.2 有参构造器
//有参构造器
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; // *this表示对象指针,因此只赋值了地址
}
外部数据流构造函数
// 外部数据流构造函数
CMatrix::CMatrix(const char * strPath):m_nRow(0),m_nCol(0),m_pData(0)
{
ifstream cin(strPath); //通过ifstream定义输入流对象
//将输入流地址赋值给对象指针
cin>>*this;
}
2.2析构函数(Destructor)
析构函数与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。
// 析构函数
// 析构函数无参, 不可重载
// 程序在对象销毁前自动调用析构函数,无需手动调用
CMatrix::~CMatrix()
{
Release(); // 调用类方法
}
2.3 CMatrix
对象方法
2.3.1对象初始化
// 新建类对象方法
bool CMatrix::Create(int nRow, int nCol, double *pData)
{
// 首先在构造前需将其数据指针赋值为空
Release();
// 赋值
m_pData = new double[nRow*nCol];
m_nRow = nRow;
m_nCol = nCol;
if(pData != NULL)
{
// 将传入的pData赋值给类内部变量m_pData(内存拷贝的方式)
memcpy(m_pData, pData, nRow*nCol*sizeof(double));
}
}
2.3.2 对象销毁方法
// 销毁对象方法
void CMatrix::Release()
{
//如果指针非空将其指向空
if(m_pData != NULL)
{
delete []m_pData;
m_pData = NULL;
}
//初始化0
m_nRow = m_nCol = 0;
}
2.4 CMatrix
运算符重载
在c++中,可以重定义或重载大部分内置的运算符。这样就可以使用自定义类型的运算符,实现更为复杂的运算。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
2.4.1 赋值运算符重载
//运算符重载(对已有运算符重新定义,赋予其另一种功能,以适应不同的数据类型)
// "="重载
CMatrix& CMatrix::operator=(const CMatrix& m)
{
//如果自己对自己赋值就直接跳过,这是因为Create方法会首先调用Release(),导致原数据被释放
if(this!=&m){
// 这里的“=”赋值采用Create方法,是深拷贝
Create(m.m_nRow, m.m_nCol, m.m_pData);
}
return *this;
}
2.4.2关系运算符重载
运算符”==“重载
//运算符”==“重载
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);
}
2.4.3运算符重载
// "+="重载
CMatrix& CMatrix::operator+=(const CMatrix& m)
{
//assert断言函数,对括号内的假设进行判断,假如不符合条件就抛出错误,终止程序运行
// 这里的断言函数保证运算符两边的size相等
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断言函数,对括号内的假设进行判断,假如不符合条件就抛出错误,终止程序运行
// 这里的断言函数保证运算符两边的size相等
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;
}
"-"重载
// "-"重载
CMatrix operator-(const CMatrix& m1, const CMatrix& m2)
{
//"-="重载实现"-"重载
CMatrix m3(m1);
m3 -= m2;
return m3;
}
2.4.4 操作符重载
下标操作符[]重载
// 下标操作符[]重载
double & CMatrix::operator[](int nIndex)
{
//保证下标不越界
assert(nIndex<m_nRow*m_nCol);
return m_pData[nIndex];
}
操作符()重载
// 操作符()重载
// a(2,5)读取矩阵a的第二行第五列
double & CMatrix::operator()(int nRow, int nCol)
{
//保证下标不越界
assert(nRow * m_nCol + nCol < m_nRow * m_nCol);
return m_pData[nRow * m_nCol + nCol];
}
操作符”>>“重载
// 重载操作符”>>“
// 使得“>>”操作符能够读取 CMatrix 数据类型
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;
}
操作符”<<“重载
// 重载操作符”<<“
// 使得“<<”操作符能够打印 CMatrix 数据类型
ostream & operator<<(ostream& os, const CMatrix &m)
{
os<<"size:["<<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::operator double()
{
double dS=0;
// 将类型转换重载为矩阵所有元素相加
for(int i=0;i<m_nRow*m_nCol;i++){
dS+=m_pData[i];
}
return dS;
}
3.主函数测试样例main.cpp
#include <iostream>
#include <stdio.h>
#include "cmatrix.h"
using namespace std;
int main(int argc, char** argv) {
double pData[10]={2,3,4,5};
CMatrix m1, m2(2,5,pData), m3("../1.txt"), m4(m2);
cin>>m1;
m2.Set(1,3,10);
m4=m3;
m4[2] = m4 + 1;
cout<<m1<<m2<<m3<<m4;
if(m4 == m3)
{
cout<<"Error !"<<endl;
}
m4 -= m3;
cout<<"m4 -= m3:\n"<<m4;
cout<<"m4 -= m3 = "<<(double)m4<<endl;
// double data[10] = {1,2,3,4,5,6}
// CMatrix ms[3] = {CMatrix(), CMatrix(), "../1.txt"}
return 0;
}
运行结果:
pi@raspberrypi:~/Desktop/cpp/build $ ./main
2 2
4 4 4 4
size:[2,2]
4 4
4 4
size:[2,5]
2 3 4 5 0
0 0 0 10 0
size:[2,2]
1 2
3 4
size:[2,2]
1 2
11 4
m4 -= m3:
size:[2,2]
0 0
8 0
m4 -= m3 = 8
pi@raspberrypi:~/Desktop/cpp/build $
值得注意的是,在执行m4 + 1(或者1 + m4)运算的过程中,程序会默认先将m4解析为(double)m4,然后再加1。(这里先留一个小疑问)
还有就是对于双目运算符"+" , "-"的重载,必须定义在类的外部,否则编译过程中会报错。