当前位置:   article > 正文

C++类与对象学习总结_类与对象实验报告总结

类与对象实验报告总结

一,基础知识

1,类的定义

     类是对具有相同属性和行为的一组对象的抽象与统一描述。是用户自定义的数据类型。类的定义包括行为和属性两个部分。属性以数据表示,行为通过函数实现。

2,(1)C ++中定义类的说明语句一般形式为:

  1. class <类名>
  2. {
  3. public:
  4. //公有段数据成员和成员函数;
  5. protected:
  6. //保护段数据成员和成员函数;
  7. private:
  8. //私有段数据成员和成员函数;
  9. }; //分号不得省略!!!

关键字private用于声明私有成员,私有成员只能在类中可见,不能在类外或派生类中使用。如果私有成员放在第一段,则可以忽略关键字private

protected声明保护成员。保护成员在类和它的派生类中可见。

public 声明公有成员。公有成员是类的接口,在类中和类外可见。

(2)注意事项:

  类的成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员  类定义必须以分号“;”结束  。

 类与结构体的区别:

 没有明确指定类成员的访问权限时,C ++结构体的成员是公有的,而类的成员是私有的。

(3)数据成员和成员函数

  1. class Date
  2. {public:
  3. void SetDate(int y,int m,int d);
  4. int IsLeapYear();
  5. void PrintDate();
  6. private:
  7. int year,month,day;
  8. };

在这个类的定义中

定义了三个私有数据成员year,month,day;SetDate函数是用于获取对象的值,设置日期;函数IsLeapYear用于判断是否是闰年; PrintfDate函数用于输出日期。在类的说明语句中没有给出函数的实现。

成员函数在类外的定义使用作用域区分符进行说明,此时函数头的形式为:

返回类型  类名::函数名(参数表) 

则以上的三个成员函数可以输入为:
  1. void Date::SetDate(int y,int m,int d)
  2. {year=y;
  3. month=m;
  4. day=y;
  5. }
  6. int Date::IsLeapYear()
  7. {return(year%4==0&&year%100!=0)||(year%400==0);}
  8. Void Date::PrintDate()
  9. {cout<<year<<”.”<<month<<”.”<<day;}

简单的成员函数实现可以在类中定义,此时,编译器作为内联函数处理例如,成员函数的Setdate在类中写成:

  1. // ...
  2. public
  3. void SetDate(int y,int m,int d)
  4. {year = y;
  5. month=m;
  6. day= d;
  7. }
  8. // ...

成员函数有两个作用:

一是操作数据成员,包括访问和修改数据成员;

二是用于协同不同的对象操作,称为传递消息

3,对象

(1)对象是类的实例或实体。类与对象的关系,如同C ++基本数据类型和该类型的变量之间的关系。

           对象定义的格式:类名对象名1,对象名2,...,对象名n;

(2)访问对象成员

圆点访问形式:对象名公有成员。

指针访问形式:对象指针变量名 - >公有成员

例:
  1. #include<iostream>
  2. using namespace std;
  3. class ptr_access
  4. {public:
  5. void setvalue(float a, float b) { x=a; y=b; }
  6. float Getx() {return x;}
  7. float Gety() {return y;}
  8. void print()
  9. {
  10. cout<<"x="<<x<<endl;
  11. cout<<"y="<<y<<endl;
  12. }
  13. private: //私有数据成员
  14. float x,y;
  15. };
  16. int main()
  17. {
  18. float a1,a2;
  19. ptr_access *ptr=new ptr_access;
  20. ptr->setvalue(2,8);
  21. //通过指针访问公有成员函数
  22. ptr->print();
  23. a1=(*ptr).Getx();
  24. //通过公有成员函数访问私有数据成员
  25. a2=(*ptr).Gety();
  26. cout<<"a1="<<a1<<endl;
  27. cout<<"a2="<<a2<<endl;
  28. return 0;
  29. }

4,构造函数和析构函数

(1)构造函数的特点

构造函数是用于创建对象的特殊成员函数 ,当创建对象时,系统自动调用构造函数 。

构造函数的作用是: 为对象分配空间;对数据成员赋初值;请求其他资源 。

没有用户定义的构造函数时,系统提供缺省版本的构造函数 。

构造函数名与类名相同:类名 。

构造函数可以重载。

构造函数可以有任意类型的参数,但没有返回类型。

例:为类Date建立一个构造函数。

  1. #include <iostream.h>
  2. class Date {
  3. public:
  4. Date(); // 无参构造函数
  5. Date(int y,int m,int d);
  6. void showDate();
  7. private:
  8. int year, month, day;
  9. };
  10. Date::Date() // 构造函数的实现
  11. { year=0; month=0; day=0; }
  12. Date::Date(int y,int m,int d)
  13. { year=y; month=m; day=d; }
  14. inline void Date::showDate()
  15. { cout<<year<<"."<<month<<"."<<day<<endl; }
  16. int main()
  17. {
  18. Date a_date,bDate(2014,3,25);
  19. a_date.showDate();
  20. b_date.showDate();
  21. return 0;
  22. }

(2)通常,利用构造函数创建对象有以下两种方法:

a 利用构造函数直接创建对象.其一般形式为:
   类名  对象名[(实参表)];
这里的“类名”与构造函数名相同,“实参表”是为构造函数提供的实际参数。 
b 利用构造函数创建对象时,通过指针和new来实现。其一般语法形式为:

  类名 *指针变量 = new 类名[(实参表)];

(3)构造函数初始化成员的两种方法

A 使用构造函数的函数体进行初始化:

  1. class Date
  2. {
  3. int d, m, y;
  4. public:
  5. Date(int dd, int mm, int yy)
  6. {
  7. d=dd;
  8. m=mm;
  9. y=yy;
  10. }
  11. Date(int dd, int mm)
  12. {
  13. d=dd;
  14. m=mm;
  15. }
  16. }

B 使用构造函数的初始化列表进行初始化 :

格式:

funname(参数列表):初始化列表

{  函数体,可以是空函数体  } 
初始化列表的形式: 

成员名1(形参名1),成员名2(形参名2),成员名n(形参名n) 

例:

  1. class Date
  2. {
  3. int d, m, y;
  4. public:
  5. Date(int dd, int mm, int yy):d(dd),m(mm),y(yy)
  6. { }
  7. Date(int dd, int mm): d(dd),m(mm)
  8. { }
  9. }

注:必须使用参数初始化列表对数据成员进行初始化的几种情况:

a 数据成员为常量
b 数据成员为引用类型

c 数据成员为没有无参构造函数的类的对象

(4)构造函数的重载
  1. class Date
  2. { public:
  3. Date();
  4. Date(int);
  5. Date(int,int);
  6. Date(int,int,int);
  7. ~Date();
  8. //...
  9. }
  10. //...
  11. void f()
  12. { Date d;//调用Date();
  13. Date d1(2000);//调用Date(int);
  14. Date d1(2000,1);//调用Date(int,int);
  15. Date d1(2000,1,1);//调用Date(int,int,int);
  16. }

(5)析构函数

 析构函数是用于取消对象的成员函数,当一个对象作用域结束时,系统自动调用析构函数。

 析构函数的作用是进行对象消亡时的清理工作。

 没有用户定义析构函数时,系统提供缺省版本的析构函数。

 析构函数名为: ~ 类名。

 析构函数没有参数,也没有返回类型。

5,需要显式引用this指针的三种情况

(1)在类的非静态成员函数中返回类对象本身或对象的引用的时候,直接使用 return *this,返回本对象的地址时,return this。
(2)当参数与成员变量名相同时,如this->x = x,不能写成x = x。

(3)避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。

6,复制构造函数

复制构造函数用一个已有同类对象创建新对象进行数据初始化
语法形式

类名 :: 类名(const  类名  &  引用名  ,  …);

(1)复制构造函数的特点: 

a 复制构造函数名与类名相同,并且也没有返回值类型。

b 复制构造函数可写在类中,也可以写在类外。

c 复制构造函数要求有一个类类型的引用参数。

d 如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。 

(2)三种由编译系统自动调用复制构造函数的情况

a 声明语句中用类的一个已知对象初始化该类的另一个对象时。 
b 当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。 
c 当对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。

(3)浅复制和深复制

(1)关于浅复制

在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。
即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容

默认复制构造函数所进行的是简单数据复制,即浅复制 

(2)关于深复制

通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制。 

自定义复制构造函数所进行的复制是浅复制。 

7,常成员

(1)常数据成员

    使用const说明的数据成员称为常数据成员。

    如果在一个类中说明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该成员赋值。 

  1. #include<iostream>
  2. using namespace std;
  3. class Mclass
  4. { public :
  5. int k;
  6. const int M; //说明常数据成员
  7. Mclass() : M(5) { } //用初始式对常数据成员赋值
  8. void testFun()
  9. { //M++; //错误,不能在成员函数中修改常数据成员
  10. k++; //可以修改一般数据成员
  11. }
  12. } ;
  13. int main()
  14. { Mclass t1, t2;
  15. t1.k=100;
  16. //t1.M=123; //错误,不能在类外修改常数据成员
  17. cout<<"t1.k="<<t1.k<<'\t'<<"t1.M="<<t1.M<<endl;
  18. t2.k=200; t2.testFun();
  19. cout<<"&t1.M="<<&t1.M<<endl;
  20. cout<<"t2.k="<<t2.k<<'\t'<<"t2.M="<<t2.M<<endl;
  21. cout<<"&t2.M="<<&t2.M<<endl;
  22. }

(2)常对象

如果在说明对象时用const修饰,则被说明的对象为常对象。
常对象的说明形式如下:
      类名 const 对象名[(参数表)];
  或者
      const  类名 对象名[(参数表)];
  在定义常对象时必须进行初始化,而且不能被更新。
    说明:
   (1)C++不允许直接或间接更改常对象的数据成员。

   (2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。

(3)常成员函数

在类中使用关键字const说明的函数为常成员函数,常成员函数的说明格式如下:
       类型说明符 函数名(参数表) const;
  const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。 

  常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)

  1. #include<iostream>
  2. using namespace std ;
  3. class Simple
  4. { int x, y ;
  5. public :
  6. void setXY ( int a, int b) { x = a ; y = b ; }
  7. void printXY() { cout << x << "," << y << endl ; }
  8. void constFun ( ) const
  9. { x ++ ; y ++ ; }//非法
  10. };

8,静态成员

类成员冠以static声明时,称为静态成员。 

静态数据成员为同类对象共享。

静态成员函数与静态数据成员协同操作

静态成员不属于某一个单独的对象,而是为类的所有对象所共有

静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员: 保证在不依赖于某个对象的情况下,访问静态数据成员

9,友元

如果在本类(类A)以外的其他地方定义了一个函数(函数B)
这个函数可以是不属于任何类的非成员函数,
也可以是其他类的成员函数,
在类体中用friend对其(函数B)进行声明,此函数就称为本类(类A)的友元函数。

友元函数(函数B)可以访问这个类(类A)中的私有成员

例:

  1. class A
  2. { private:
  3. int i ;
  4. friend void FriendFun(A * , int) ;
  5. public:
  6. void MemberFun(int) ;
  7. } ;
  8. void FriendFun( A * ptr , int x )
  9. { ptr -> i = x ; } ;  
  10. void A:: MemberFun( int x )
  11. { i = x ; } ;

10,类的包含

类的包含是程序设计中一种软件重用技术。即定义一个新的类时,通过编译器把另一个类 “抄”进来。 

当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化的语法形式:

构造函数 ( 形参表 ) : 对象成员1(形参表 ) , … , 对象成员n (形参表 ) ; 

初始化顺序:先初始化被包含的对象成员再初始化本身的数据成员。

二,例题总结

在这一章的学习中就完整的写出了Student这一个类

  1. #include<iostream>
  2. #include<string>
  3. #include<algorithm>
  4. using namespace std;
  5. class Student
  6. {
  7. string name;
  8. int no;
  9. int score[3];
  10. float average;
  11. int order;
  12. public:
  13. Student(int id,string na,int x,int y,int z):name(na),no(id)
  14. {
  15. score[0]=x;score[1]=y;score[2]=z;
  16. order=1;average=(score[0]+score[1]+score[2])/3;
  17. }
  18. Student()
  19. {
  20. score[0]=score[1]=score[2]=0;
  21. order=1;
  22. average=0;
  23. }
  24. int getNo(){return no;}
  25. float getAverage(){return average;}
  26. void setAverage(int avg) {average=avg;}
  27. void setOrder(int x){order=x;}
  28. int getOrder(){return average;}
  29. string getName(){return name;}
  30. void setName(string name){this->name=name;}
  31. void display();
  32. };
  33. void Student::display()
  34. {
  35. cout<<name<<"\t"<<score[0]<<"\t"<<score[1]<<"\t"<<score[2]<<"\t"<<average<<"\t\t"<<order<<endl;
  36. }
  37. bool cmp1(Student stu1,Student stu2)
  38. {
  39. if(stu1.getAverage()-stu2.getAverage()>=1e-9)
  40. return 1;
  41. }
  42. bool cmp2(Student stu1,Student stu2)
  43. {
  44. return stu1.getNo()<stu2.getNo();
  45. }
  46. class Reportcard
  47. {
  48. Student list[60];
  49. int n;
  50. public:
  51. Reportcard():n(0){};
  52. void add();
  53. //void deleteStu;
  54. void query();
  55. void change();
  56. void display(int flag);
  57. int search(int no);
  58. void sortList();
  59. };
  60. void Reportcard::add()
  61. {
  62. int no,x,y,z;
  63. string name;
  64. //system("cls");//清屏
  65. cout<<"按学号、姓名、数学、英语、c++顺序输入学生信息,学号输-1表示结束"<<endl;
  66. while((cin>>no)&&no!=-1)
  67. {
  68. cin>>name>>x>>y>>z;
  69. Student s(no,name,x,y,z);
  70. list[n++]=s;
  71. /*for(int i=0,i<n,i++)
  72. list[i].display();
  73. */
  74. sortList();
  75. }
  76. }
  77. void Reportcard::sortList()
  78. {
  79. sort(list,list+n,cmp1);
  80. int i;
  81. for(i=0;i<n;i++)
  82. list[i].setOrder(i+1);
  83. }
  84. void Reportcard::display(int flag)
  85. {
  86. if(flag) sort(list,list+n,cmp2);
  87. else sort(list,list+n,cmp1);
  88. cout<<"姓名"<<"\t"<<"学号"<<"\t"<<"数学"<<"\t"<<"英语"<<"\t"<<"c++"<<"\t"<<"平均成绩"<<"\t"<<"名次"<<endl;
  89. for(int i=0;i<n;i++)
  90. list[i].display();
  91. }//flag=1,按学号排列;flag=2,按名次排列
  92. int Reportcard::search(int no)
  93. {
  94. int i;
  95. for(i=0;i<n;i++)
  96. {
  97. if(list[i].getNo()==no)
  98. return i;
  99. }
  100. return -1;
  101. }
  102. void Reportcard::query()
  103. {
  104. int no,i;
  105. //system("cls");
  106. cout<<"请输入要查询同学的学号,按-1结束查询";
  107. while(cin>>no&&no!=-1)
  108. {
  109. i=search(no);
  110. if(i!=-1)
  111. {
  112. cout<<"姓名"<<"\t"<<"学号"<<"\t"<<"数学"<<"\t"<<"英语"<<"\t"<<"c++"<<"\t"<<"平均成绩"<<"\t"<<"名次"<<endl;
  113. list[i].display();
  114. cout<<"请输入要查询同学的学号,按-1结束查询";
  115. }
  116. else
  117. cout<<"学号输入有误,请重输,输入-1结束查询"<<endl;
  118. }
  119. }
  120. /*void Reportcard::deleteStu()
  121. {
  122. }*/
  123. int main()
  124. {
  125. Student s(1,"ff",66,77,88);
  126. s.display();
  127. cout<<s.getAverage()<<endl;
  128. s.setName("ff");
  129. s.display();
  130. Reportcard c;
  131. c.add();
  132. c.display(1);
  133. c.query();
  134. c.display(0);
  135. return 0;
  136. }

1,在定义类的过程中,每增加一个功能都要首先把它调通,每写一个类,也要先把它调试通,采用滚雪球的方法一步步完成。

2,描述信息的类重点是描述信息,成员函数一定有一组get和set函数,以及构造函数。

3,操作类关注于功能的实现,数据成员一定是集合类型和对象数组。

三,学习心得

    从面向过程向面向对象思维的转变,还是有一定难度的。在这章学习中,对管理信息系统开发基本步骤有了基本的了解。先了解系统要实现什么功能;再对系统功能进行分类汇总;然后设计操作类;再设计数据类;然后整合操作类所需的数据,设计数据类,明确如何修饰数据成员,确定数据类的成员函数;最后编码调试数据类和操作类。这种思维模式让我更好的完成向面向对象思维的转变。最后通过老师讲解的ATM的案例,对软件开发的思路也有了初步的了解。随着课程的进行,要做的东西越来越难,越来越复杂,加油!



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

闽ICP备14008679号