当前位置:   article > 正文

C++程序设计 —— 实验一:类与对象_c++类和对象实验体会

c++类和对象实验体会

目录

一、类和对象

二、构造函数

三、析构函数

四、运算符重载

五、友元函数

六、实验结果

七、实验总结


 一、类和对象

1、类的定义:

        在C++中, 用 "" 来描述 "对象",所谓的"对象"是指现实世界中的一切事物。那么类就可以看做是对相似事物的抽象,找到这些不同事物间的共同点,如自行车和汽车,首先他们都属于"对象", 并且具有一定得相同点,和一些不同点, 相同点如他们都有质量、都有轮子, 都是属于交通工具等。"都有质量"、"两个轮子"属于这个对象的属性, 而"都能够当做交通工具"属于该对象具有的行为, 也称方法。

        在面向对象的程序设计语言中,程序模块是类构成的。类是对逻辑上相关的函数与数据的封装,它是对问题的抽象描述。以我们平常熟知的矩阵为例,矩阵属于对象,要定义一个矩阵类,矩阵需要横坐标和纵坐标,以及矩阵内的值。

用C++语言定义一个矩阵类,如下所示:

  1. // 举一个例子
  2. class CMatrix{
  3. public:
  4. void Set(int nRow, int nCol, double dVale); //set方法,修改矩阵
  5. void Show(); //get方法,输出展示矩阵
  6. private:
  7. int m_nRow; //行,横坐标
  8. int m_nCol; //列,纵坐标
  9. double *m_pData; //矩阵数据值
  10. };

        这里,封装了矩阵的数据和行为,分别是称为CMatrix类的数据成员和函数成员。public是公有类型,private是私有类型,除了这两个之外,还有protected保护类型。

  • public公有类型:公有类型成员定义了类的外部接口。共有成员用public关键字声明,在类外只能访问类的公有成员。对于类,从外部只能调用set()和show()这里两个公共类型的函数成员来改变或观察矩阵。
  • private私有类型:private后面声明的就是类的私有成员。如果私有成员紧接着类名称,则关键字可以省略。私有成员只能被本类的成员函数访问,来自类外部的任何访问都是非法的。对于CMatrix类,m_nRow、m_nCol、m_pData都是私有成员。
  • protected保护类型:保护类型成员的性质和私有成员性质相似,其差别在于继承过程中对产生的新类影响不同。

2、对象

  ①声明对象

  如下声明了一个矩阵类型的对象myMatrix:

  1. // 类名  对象名;
  2. CMatrix myMatrix;

  ②访问对象成员

  1. // 对象名.数据成员名
  2. myMatrix.show();

3、完整定义矩阵类: CMtrix.cpp

  1. class CMatrix{
  2. public:
  3. // 构造函数
  4. CMatrix();
  5. CMatrix(int nRow, int nCol, double *pData = NULL);
  6. CMatrix(const CMatrix& m);
  7. CMatrix(const char * strPath);
  8. //析构函数
  9. ~CMatrix();
  10. void release();
  11. //初始化
  12. bool Create(int nRow, int nCol, double *pData = NULL);
  13. void Set(int nRow, int nCol, double dVale);
  14. //运算符重载
  15. CMatrix& operator+(const CMatrix& m);
  16. CMatrix& operator-(const CMatrix& m);
  17. CMatrix& operator+=(const CMatrix& m);
  18. CMatrix& operator-=(const CMatrix& m);
  19. CMatrix& operator=(const CMatrix& m);
  20. double& operator[](int nIndex){
  21. return m_pData[nIndex];
  22. }
  23. double& operator()(int nRow,int nCol){
  24. return m_pData[nRow*nCol + nCol];
  25. }
  26. bool operator ==(const CMatrix& m);
  27. bool operator !=(const CMatrix& m);
  28. bool operator >(const CMatrix& m);
  29. bool operator <(const CMatrix& m);
  30. operator double();
  31. // 友元函数
  32. friend istream& operator>>(istream& is,CMatrix& m);//全局函数
  33. friend ostream& operator<<(ostream& os,const CMatrix& m);
  34. private:
  35. int m_nRow;
  36. int m_nCol;
  37. double *m_pData;
  38. };
  39. CMatrix operator+(const CMatrix& m1,const CMatrix& m2);
  40. inline void CMatrix::Set(int nRow, int nCol, double dVale){
  41. m_pData[nRow*m_nCol + nCol] = dVale;
  42. }

二、构造函数

1、默认构造函数:不带参数的构造函数

  1. // 方法一
  2. CMatrix::CMatrix():m_nRow(0),m_nCol(0),m_pData(NULL){}
  3. // 方法二
  4. CMatrix::CMatrix(){
  5. m_nRow = 0;
  6. m_nCol = 0;
  7. *m_pData = NULL; //设为空,避免运行出错
  8. }

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

  1. // 方法一
  2. CMatrix::CMatrix(int nRow, int nCol, double *pData):m_pData(NULL){
  3. m_nRow = nRow;
  4. m_nCol = nCol;
  5. memcpy(m_pData,pData,nRow*nCol*sizeof(double));
  6. }
  7. // 方法二
  8. CMatrix::CMatrix(int nRow, int nCol, double *pData):m_pData(NULL){
  9. Create(nRow, nCol, pData);
  10. }

3、带文件路径参数的构造函数

  1. CMatrix::CMatrix(const char * strPath){
  2. m_pData = NULL;
  3. m_nRow = m_nCol = 0;
  4. ifstream cin(strPath);
  5. cin >> *this;
  6. }

4、拷贝构造函数

  1. // 方法一
  2. CMatrix::CMatrix(const CMatrix& m):CMatrix(m.m_nRow, m.m_nCol, m.m_pData){
  3. m_nRow = m.m_nRow;
  4. m_nCol = m.m_nCol;
  5. memcpy(m_pData,m.m_pData,m.m_nRow*m.m_nCol*sizeof(double));
  6. }
  7. // 方法二
  8. CMatrix::CMatrix(const CMatrix& m):CMatrix(m.m_nRow, m.m_nCol, m.m_pData){
  9. m.m_pData = NULL;
  10. *this = m;
  11. }

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中

  1. // 创建数据函数
  2. bool CMatrix::Create(int nRow, int nCol, double *pData){
  3. release();
  4. m_nRow = nRow;
  5. m_nCol = nCol;
  6. m_pData = new double[nRow*nCol];
  7. if(pData != NULL and m_pData != NULL){ //m_pData不为空,即分配了空间内存
  8. memcpy(m_pData,pData,nRow*nCol*sizeof(double));
  9. return true;
  10. }
  11. return false;
  12. }

三、析构函数

       与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。以C++语言为例:析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

1、Release(): 将内存释放,并将行列设置为0

  1. // 空间释放函数
  2. void CMatrix::release(){
  3. if(m_pData){
  4. delete []m_pData;
  5. m_pData = NULL; //好习惯,保证释放空间
  6. }
  7. m_nRow = m_nCol = 0;
  8. }

2、~CMatrix(): 定义析构函数,调用release()

  1. CMatrix::~CMatrix(){
  2. release();
  3. }

四、运算符重载

1、算术运算符重载:+, -, +=, -=

  1. // + 算术运算符重载:带一个参数
  2. CMatrix& CMatrix::operator+(const CMatrix& m){
  3. assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol); //断言函数,判断两个矩阵类是否满足运算前提
  4. for(int i = 0;i < m_nRow*m_nCol; i++){
  5. m_pData[i] += m.m_pData[i];
  6. }
  7. return *this;
  8. }
  9. // + 算术运算符重载:两个参数
  10. CMatrix operator+(const CMatrix& m1,const CMatrix& m2){
  11. CMatrix m3(m1);
  12. m3 += m2;
  13. return m3;
  14. }
  15. // - 算术运算符重载
  16. CMatrix& CMatrix::operator-(const CMatrix& m){
  17. assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol);
  18. for(int i = 0;i < m_nRow*m_nCol; i++){
  19. m_pData[i] -= m.m_pData[i];
  20. }
  21. return *this;
  22. }
  23. // += 算术运算符重载
  24. CMatrix& CMatrix::operator+=(const CMatrix& m){
  25. assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol);
  26. for(int i = 0;i < m_nRow*m_nCol; i++){
  27. m_pData[i] += m.m_pData[i];
  28. }
  29. return *this;
  30. }
  31. // -= 算术运算符重载
  32. CMatrix& CMatrix::operator-=(const CMatrix& m){
  33. assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol);
  34. for(int i = 0;i < m_nRow*m_nCol; i++){
  35. m_pData[i] -= m.m_pData[i];
  36. }
  37. return *this;
  38. }


2、关系运算符重载:>, <, ==,!=

  1. // == 关系运算符重载
  2. bool CMatrix::operator ==(const CMatrix& m){
  3. if(!(m_nRow==m.m_nRow && m_nCol==m.m_nCol))
  4. return false;
  5. for(int i = 1;i < m_nRow*m_nCol; i++){
  6. if(m_pData[i] != m.m_pData[i])
  7. return false;;
  8. }
  9. return true;
  10. }
  11. // != 关系运算符重载
  12. bool CMatrix::operator !=(const CMatrix& m){
  13. return !(*this == m);
  14. }
  15. // > 关系运算符重载
  16. bool CMatrix::operator >(const CMatrix& m){
  17. assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol);
  18. for(int i = 1;i < m_nRow*m_nCol; i++){
  19. if(m_pData[i] > m.m_pData[i])
  20. return true;;
  21. }
  22. return false;
  23. }
  24. // < 关系运算符重载
  25. bool CMatrix::operator <(const CMatrix& m){
  26. assert(m_nRow==m.m_nRow && m_nCol==m.m_nCol);
  27. for(int i = 1;i < m_nRow*m_nCol; i++){
  28. if(m_pData[i] < m.m_pData[i])
  29. return true;;
  30. }
  31. return false;
  32. }


3、下标操作符:[], ()

  1. // 下标操作符[]
  2. double& operator[](int nIndex){
  3. return m_pData[nIndex];
  4. }
  5. // 下标操作符()
  6. double& operator()(int nRow,int nCol){
  7. return m_pData[nRow*nCol + nCol];
  8. }


4、强制类型转换: double

  1. CMatrix::operator double(){
  2. double dSum = 0;
  3. for (int i = 0; i < m_nRow * m_nCol; i++) {
  4. dSum += m_pData[i];
  5. }
  6. return dSum;
  7. }


5、赋值运算符:=,尤其注意当m1=m1特殊情况的处理

  1. // 若m1 = m1情况,判断地址是否相等,不相等则进行操作
  2. CMatrix& CMatrix::operator=(const CMatrix& m){
  3. if(this != &m)
  4. Create(m.m_nRow, m.m_nCol, m.m_pData);
  5. return *this;
  6. }

五、友元函数

        类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend。

在CMatrix类定义友元函数:

friend istream& operator>>(istream& is,CMatrix& m);
friend ostream& operator<<(ostream& os,const CMatrix& m);

1、输入运输符: >>

  1. istream& operator>>(istream& is,CMatrix& m){
  2. is>>m.m_nRow>>m.m_nCol;
  3. m.Create(m.m_nRow,m.m_nRow);
  4. for(int i = 1;i < m.m_nRow*m.m_nCol; i++)
  5. is>>m.m_pData[i];
  6. return is;
  7. }

2、输出运输符:<<

  1. ostream& operator<<(ostream& os,const CMatrix& m){
  2. os<<m.m_nRow<<" "<<m.m_nCol<<endl;
  3. double * pData = m.m_pData;
  4. for(int i=0;i<m.m_nRow;i++){
  5. for(int j = 0; j < m.m_nCol; j++){
  6. os<<*pData++<<" ";
  7. }
  8. os<<endl;
  9. }
  10. return os;
  11. }

六、实验结果

1、测试代码

  1. #include <iostream>
  2. #include <stdio.h>
  3. #include "CMatrix.h"
  4. using namespace std;
  5. int main(){
  6. cout<<"对象声明:m1,m2,m3,m4"<<endl;
  7. cout<<"m1 调用默认构造函数:不带参数的构造函数"<<endl;
  8. cout<<"m2 调用委托构造函数:带行列及数据指针等参数的构造函数"<<endl;
  9. cout<<"m3 调用带文件路径参数的构造函数"<<endl;
  10. cout<<"m4 调用拷贝构造函数"<<endl;
  11. double pData[10]={1,2,3,4,5,6,7,8};
  12. CMatrix m1,m2(2,4,pData), m3("d:\\data.txt"),m4(m2);
  13. cout<<"输入m1的数据内容:行 列 数组"<<endl;
  14. cin>>m1;
  15. m2.Set(1,3,10);// 修改m2的值
  16. cout<<"输出m1"<<endl;
  17. cout<<m1<<endl;
  18. cout<<"输出m2"<<endl;
  19. cout<<m2<<endl;
  20. cout<<"输出m3"<<endl;
  21. cout<<m3<<endl;
  22. cout<<"输出m4"<<endl;
  23. cout<<m4<<endl;
  24. cout<<"测试关系运算符重载:= 令m4=m3,输出m4"<<endl;
  25. m4 = m3;
  26. cout<<m4<<endl;
  27. cout<<"测试下标操作符:[] ,输出m4"<<endl;
  28. m4[2]= m4 + 1;
  29. cout<<m4<<endl;
  30. if(m4==m3){
  31. cout<<"Error !"<<endl;
  32. }
  33. cout<<"测试关系运算符重载:+= 令m4+=m3,输出m4"<<endl;
  34. m4 += m3;
  35. cout<<m4<<endl;
  36. cout<<"sum of m4 = "<<(double)m4<<endl;
  37. return 0;
  38. }

2、测试结果

七、实验总结

        本次实验,实践了C++类与对象,学习使用了默认构造函数、委托构造函数、带文件路径参数的构造函数、拷贝构造函数、析构函数、友元函数以及运算符重载。我觉得最有收获的就是运算符重载,学习到了方法的重载,如果不是老师的讲解,我可能也不会知道,原来这些基本的运算法也可以根据自己写的对象来进行调整设计,重载方法。因为之前学过Java的关系,所以能够理解类和对象,没有什么学习困难,实验过程很顺利!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/928818
推荐阅读
相关标签
  

闽ICP备14008679号