目录
一、类和对象
二、构造函数
三、析构函数
四、运算符重载
五、友元函数
六、实验结果
七、实验总结
1、类的定义:
在C++中, 用 "类" 来描述 "对象",所谓的"对象"是指现实世界中的一切事物。那么类就可以看做是对相似事物的抽象,找到这些不同事物间的共同点,如自行车和汽车,首先他们都属于"对象", 并且具有一定得相同点,和一些不同点, 相同点如他们都有质量、都有轮子, 都是属于交通工具等。"都有质量"、"两个轮子"属于这个对象的属性, 而"都能够当做交通工具"属于该对象具有的行为, 也称方法。
在面向对象的程序设计语言中,程序模块是类构成的。类是对逻辑上相关的函数与数据的封装,它是对问题的抽象描述。以我们平常熟知的矩阵为例,矩阵属于对象,要定义一个矩阵类,矩阵需要横坐标和纵坐标,以及矩阵内的值。
用C++语言定义一个矩阵类,如下所示:
// 举一个例子 class CMatrix{ public: void Set(int nRow, int nCol, double dVale); #set方法,修改矩阵 void Show(); #get方法,输出展示矩阵 private: int m_nRow; #行,横坐标 int m_nCol; #列,纵坐标 double *m_pData; #矩阵数据值 };
这里,封装了矩阵的数据和行为,分别是称为CMatrix类的数据成员和函数成员。public是公有类型,private是私有类型,除了这两个之外,还有protected保护类型。
2、对象
①声明对象
如下声明了一个矩阵类型的对象myMatrix:
// 类名 对象名; CMatrix myMatrix;
②访问对象成员
// 对象名.数据成员名 myMatrix.show();
3、完整定义矩阵类
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 Set(int nRow, int nCol, double dVale); void release(); CMatrix& operator+(const CMatrix& m); CMatrix& operator-(const CMatrix& m); CMatrix& operator+=(const CMatrix& m); CMatrix& operator-=(const CMatrix& m); CMatrix& operator=(const CMatrix& m); double& operator[](int nIndex){ return m_pData[nIndex]; } double& operator()(int nRow,int nCol){ return m_pData[nRow*nCol + nCol]; } bool operator ==(const CMatrix& m); bool operator !=(const CMatrix& m); bool operator >(const CMatrix& m); bool operator <(const CMatrix& m); operator double(); friend istream& operator>>(istream& is,CMatrix& m);//全局函数 friend ostream& operator<<(ostream& os,const CMatrix& m); 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 dVale){ m_pData[nRow*m_nCol + nCol] = dVale; }
1、默认构造函数:不带参数的构造函数
// 方法一 CMatrix::CMatrix():m_nRow(0),m_nCol(0),m_pData(NULL){} // 方法二 CMatrix::CMatrix(){ m_nRow = 0; m_nCol = 0; *m_pData = NULL; }
2、委托构造函数:带行、列及数据指针等参数的构造函数,并且参数带默认值
// 方法一 CMatrix::CMatrix(int nRow, int nCol, double *pData):m_pData(NULL){ m_nRow = nRow; m_nCol = nCol; memcpy(m_pData,pData,nRow*nCol*sizeof(double)); } // 方法二 CMatrix::CMatrix(int nRow, int nCol, double *pData):m_pData(NULL){ Create(nRow, nCol, pData); }
3、带文件路径参数的构造函数
CMatrix::CMatrix(const char * strPath){ m_pData = NULL; m_nRow = m_nCol = 0; ifstream cin(strPath); cin >> *this; }
4、拷贝构造函数
// 方法一 CMatrix::CMatrix(const CMatrix& m):CMatrix(m.m_nRow, m.m_nCol, m.m_pData){ m_nRow = m.m_nRow; m_nCol = m.m_nCol; memcpy(m_pData,m.m_pData,m.m_nRow*m.m_nCol*sizeof(double)); } // 方法二 CMatrix::CMatrix(const CMatrix& m):CMatrix(m.m_nRow, m.m_nCol, m.m_pData){ m.m_pData = NULL; *this = m; }
5、列表初始化成员变量:CMatrix(): m_nRow(0), m_nCol(0), m_pData(NULL)
CMatrix::CMatrix():m_nRow(0), m_nCol(0), m_pData(NULL){}
6、bool Create(int nRow, int nCol, double *pData=NULL): 先删除原有空间,根据传入行列创建空间,如果pData不为空要将pData的内容拷贝到m_pData中
// 创建数据函数 bool CMatrix::Create(int nRow, int nCol, double *pData){ release(); m_nRow = nRow; m_nCol = nCol; m_pData = new double[nRow*nCol]; if(pData != NULL and m_pData != NULL){ //m_pData不为空,即分配了空间内存 memcpy(m_pData,pData,nRow*nCol*sizeof(double)); return true; } return false; }
与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。以C++语言为例:析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。
1、Release(): 将内存释放,并将行列设置为0
// 空间释放函数 void CMatrix::release(){ if(m_pData){ delete []m_pData; m_pData = NULL; } m_nRow = m_nCol = 0; }
2、~CMatrix(): 定义析构函数,调用release()
CMatrix::~CMatrix(){ release(); }
1、算术运算符重载:+, -, +=, -=
// + 算术运算符重载:带一个参数 CMatrix& CMatrix::operator+(const CMatrix& m){ assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol); for(int i = 1;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& CMatrix::operator-(const CMatrix& m){ assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol); for(int i = 1;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 = 1;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 = 1;i < m_nRow*m_nCol; i++){ m_pData[i] -= m.m_pData[i]; } return *this; }
2、关系运算符重载:>, <, ==,!=
// == 关系运算符重载 bool CMatrix::operator ==(const CMatrix& m){ if(!(m_nRow==m.m_nRow && m_nCol==m.m_nCol)) return false; for(int i = 1;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); } // > 关系运算符重载 bool CMatrix::operator >(const CMatrix& m){ assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol); for(int i = 1;i < m_nRow*m_nCol; i++){ if(m_pData[i] > m.m_pData[i]) return true;; } return false; } // < 关系运算符重载 bool CMatrix::operator <(const CMatrix& m){ assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol); for(int i = 1;i < m_nRow*m_nCol; i++){ if(m_pData[i] < m.m_pData[i]) return true;; } return false; }
3、下标操作符:[], ()
// 下标操作符[] double& operator[](int nIndex){ return m_pData[nIndex]; } // 下标操作符() double& operator()(int nRow,int nCol){ return m_pData[nRow*nCol + nCol]; }
4、强制类型转换: double
CMatrix::operator double(){ return *m_pData; }
5、赋值运算符:=,尤其注意当m1=m1特殊情况的处理
// 若m1 = m1情况,判断地址是否相等,不相等则进行操作 CMatrix& CMatrix::operator=(const CMatrix& m){ if(this != &m) Create(m.m_nRow, m.m_nCol, m.m_pData); return *this; }
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend。
在CMatrix类定义友元函数:
friend istream& operator>>(istream& is,CMatrix& m);
friend ostream& operator<<(ostream& os,const CMatrix& m);
1、输入运输符: >>
istream& operator>>(istream& is,CMatrix& m){ is>>m.m_nRow>>m.m_nCol; m.Create(m.m_nRow,m.m_nRow); for(int i = 1;i < m.m_nRow*m.m_nCol; i++) is>>m.m_pData[i]; return is; }
2、输出运输符:<<
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; }
1、测试代码
#include <iostream> #include <stdio.h> #include "CMatrix.h" using namespace std; int main(){ cout<<"对象声明:m1,m2,m3,m4"<<endl; cout<<"m1 调用默认构造函数:不带参数的构造函数"<<endl; cout<<"m2 调用委托构造函数:带行列及数据指针等参数的构造函数"<<endl; cout<<"m3 调用带文件路径参数的构造函数"<<endl; cout<<"m4 调用拷贝构造函数"<<endl; double pData[10]={1,2,3,4,5,6,7,8}; CMatrix m1,m2(2,4,pData), m3("d:\\data.txt"),m4(m2); cout<<"输入m1的数据内容:行 列 数组"<<endl; cin>>m1; m2.Set(1,3,10);// 修改m2的值 cout<<"输出m1"<<endl; cout<<m1<<endl; cout<<"输出m2"<<endl; cout<<m2<<endl; cout<<"输出m3"<<endl; cout<<m3<<endl; cout<<"输出m4"<<endl; cout<<m4<<endl; cout<<"测试关系运算符重载:= 令m4=m3,输出m4"<<endl; m4 = m3; cout<<m4<<endl; cout<<"测试下标操作符:[] ,输出m4"<<endl; m4[2]= m4 + 1; cout<<m4<<endl; if(m4==m3){ cout<<"Error !"<<endl; } cout<<"测试关系运算符重载:+= 令m4+=m3,输出m4"<<endl; m4 += m3; cout<<m4<<endl; cout<<"sum of m4 = "<<(double)m4<<endl; return 0; }
2、测试结果
本次实验,实践了C++类与对象,学习使用了默认构造函数、委托构造函数、带文件路径参数的构造函数、拷贝构造函数、析构函数、友元函数以及运算符重载。我觉得最有收获的就是运算符重载,学习到了方法的重载,如果不是老师的讲解,我可能也不会知道,原来这些基本的运算法也可以根据自己写的对象来进行调整设计,重载方法。因为之前学过Java的关系,所以能够理解类和对象,没有什么学习困难,实验过程很顺利!