Download presentation
Presentation is loading. Please wait.
1
C++与数据结构简明教程 第五章 类和对象
2
本章内容 面向对象程序设计概述 类的定义(重点) 对象的定义和对象成员的引用(重点) 对象的初始化(重点) this指针 其它定义类的形式
静态成员 友元(友元函数和友元类) (重点) 类模板
3
5.1 面向对象程序设计概述 面向过程的程序设计方法 面向对象的程序设计方法 类和对象的概念 面向对象程序设计方法的基本特征
4
计算两矩形的面积和周长(面向过程的方法)
抽象性 是指通过对具体的实例(对象)进行分析、归纳, 抽取共性而形成概念的过程。 封装性 是指将某类对象的数据和与这些数据有关的操作结合在一起,形成一个有机的整体。 继承性 是指对已有的类进行组合或扩充产生一个新的类,原有类中有用的特性予以保留并可以在新类中直接使用的一种机制,是实现代码重用的根本保证。 多态性 是指不同的对象接收到相同的消息时,会产生不同的动作。
5
5.1 面向对象程序设计概述 1.面向过程的程序设计方法 优点 缺点 编程容易 程序结构简洁清晰 小型软件开发效率高 代码可重用性差
数据安全性差 难以开发大型软件和 图形界面的应用软件 现实世界 问题 数据描述 功能描述 变量 数组 结构 文件 函数1 函数2 …. 函数n 计算机世界 程序
6
计算两矩形的面积和周长(面向对象的方法)
抽象性 是指通过对具体的实例(对象)进行分析、归纳, 抽取共性而形成概念的过程。 封装性 是指将某类对象的数据和与这些数据有关的操作结合在一起,形成一个有机的整体。 继承性 是指对已有的类进行组合或扩充产生一个新的类,原有类中有用的特性予以保留并可以在新类中直接使用的一种机制,是实现代码重用的根本保证。 多态性 是指不同的对象接收到相同的消息时,会产生不同的动作。
7
5.1 面向对象程序设计概述 2.面向对象程序设计方法 对象:现实中存在的任何一个事物。有两种特征: 静态特征:属性 动态特征:行为 问题
现实世界 对象1 对象n … 概念:同类对象的抽象描述 概念1 概念m … 类:同类对象的抽象描述,C++中的一种自定义类型 静态特征:数据成员 动态特征:函数成员 计算机世界(程序) 类1 … 类m 对象1 对象n … 对象:类的一个实例(变量) 对象间发消息并作出响应
8
5.1 面向对象程序设计概述 面向对象程序设计方法的特性及优缺点 优点 特性 代码可重用性高 抽象性 数据安全性好 封装性
开发大型软件和图形界面的应用软件效率高 缺点 编程困难 程序结构复杂 特性 抽象性 封装性 继承性 多态性
9
5.1 面向对象程序设计概述 面向对象程序设计方法的要素:类和对象 类 对象
类是C++的一种自定义的数据类型,用于抽象地描述现实世界中的同类对象 具有抽象性和封装性 由数据成员和成员函数组成 每个成员可以有三种属性 对象 类类型的变量,对应现实中的事物个体 程序就是用计算机语言描述对象的处理过程
10
5.2 类的定义 保护成员可以像公有成员那样被继承,又能像私有成员那样仅能被本类成员函数直接访问
公有成员可以在本类的成员函数中直接使用或通过本类对象使用,成员函数常被称为操作接口。 私有成员仅能在本类的成员函数中直接使用。 class 类名 { private: 私有数据成员和成员函数的定义或说明 public : 公有数据成员和成员函数的定义或说明 protected: 保护数据成员和成员函数的定义或说明 }; 类中只给出原型说明的各成员函数的实现 访问权限 类体 分号结束 一般把类的数据成员定义为私有,以实现数据隐藏。而通过公有成员函数实现对它们的访问。
11
类的定义举例--矩形类的定义 **在定义类的数据成员时不允许赋初值。 1.定义类时对数据成员的规定 必须在类体内定义
可以是任何基本数据类型,也可以是自定义类型 不能是本类的对象或对象数组,但可以是本类的指针或引用 可以是已定义的其他类的对象,称为子对象,称为组合类 只能用static修饰,不能用其他存储类别修饰符 不能在定义时赋初值 作用域为本类的定义范围 2.定义类时对成员函数的规定 可以在类体内定义,此时默认为内联函数 也可以在类体内给出函数原型,在类体外进行完整的定义,但要在类型名和函数名之间加上“类名::”,此时若要说明成内联函数,还需加上inline。 **在定义类的数据成员时不允许赋初值。
12
注意:同一个类的每个对象都拥有属于自己的数据成员
5.3 对象的定义和对象成员的引用 1.对象 类是一种类型,这种类型的变量称为该类的对象。 2.对象的定义格式 [class] 类名 对象名表; 例:设已定义了矩形类 rect。 rect ob1, ob2[ 3 ]; rect &rd1=ob1, *pd=ob2; //ob1是类rect的对象,ob2是类rect的对象数组 //rd1是类rect的对象引用,pd是类rect的对象指针 注意:同一个类的每个对象都拥有属于自己的数据成员
13
3.对象的存储 类是一种数据类型,是抽象的。对象才是具体的。对象生命期开始时,系统为对象分配存储空间,包括数据空间和代码空间,生命期结束时,释放空间。 例:设已定义了矩形类 rect,并声明三个对象: rect r1,r2,r3; x y 对象r2 对象r3 对象r1 成员函数代码
14
说明:. 和 -> 称为成员选择运算符 4.对象成员的访问 在类范围内,类的所有成员函数可以直接访问对象的成员。
在类范围外,仅可访问对象的公有成员,访问形式为: 对象名.公有数据成员名 对象名.公有成员函数名(参数表) 对象引用名.公有数据成员名 对象引用名.公有成员函数名(参数表) 对象指针名->公有数据成员名 对象指针名->公有成员函数名(参数表) 说明:. 和 -> 称为成员选择运算符
15
例:矩形类及其对象使用举例。
16
类和对象使用举例1 问题:给定年月日编程实现如下功能: 1。判断该年是否是闰年 2。显示年月日 3。显示中国成立于某年某月某日 问题分析:
1。涉及的对象:日期,如: 2。对象属性:年、月、日 3。对象行为:(1)判断闰年(2)显示年、月、日 (3)获取年、月、日 (4)辅助行为:设置年、月、日
17
类和对象使用举例1 //例5.1:日期类的定义,保存为头文件:date.h #include <iostream.h>
class Date { private: int year, month, day; public: void SetDate(int y=2002,int m=1,int d=1) {year=y;month=m>0&&m<13?m:1;day=d;} int IsleapYear() {return(year%400==0)||(year%4==0&&year%100!=0);} int GetYear(){return year;} int GetMonth(){return month;} int GetDay(){return day;} void Print() {cout<<year<<'.'<<month<<'.'<<day<<erdl;} };
18
类和对象使用举例1 // 例5.3:日期类对象的使用 #include <iostream.h>
#include “ date.h ” //包含日期类定义的头文件 void main() { Date d1,d2; d1.SetDate(2002,2,11); d2.SetDate(2000,10,1); if(d2.IsleapYear()) cout<<d2.GetYear()<<"是闰年.\n"; else cout<<d2.GetYear()<<"不是闰年.\n"; d1.Print();d2.Print(); Date *dp=&d2; dp->SetDate(1949,10,1); // (*dp).SetDate(1949,10,1); cout<<"中华人民共和国成立于"<<dp->GetYear()<<"年" <<dp->GetMonth()<<"月"<<dp->GetDay()<<"日"<<endl; }
19
类和对象使用举例2 问题:对3名学生5门课的成绩编程实现如下功能: 1。计算每名学生5门课的平均成绩; 2。按规定格式输出3名学生的平均成绩
3。按规定格式显示学生平均成绩表 问题分析: 1。涉及的对象:学生,如:李和平,81,82,87,84,85 2。对象属性:姓名、课程数、各科成绩 3。对象行为:(1)设置考试信息(2)计算平均成绩(3)格式化显示学生成绩信息
20
类和对象使用举例2 // 例5.2:学生成绩类的定义示例 #include <iostream.h>
#include <iomanip.h> class Student { int exams_taken; //考试的课程数 float *marks; //各科考试成绩 public: char *name; //学生姓名 void Setstudent(int e,float *m) //设置考试信息 {exams_taken=e;marks=m;} float average_mark(); //计算平均分函数原型说明 void format(); //格式化输出函数原型说明 void Print(); //显示学生信息函数原型说明 };
21
类和对象使用举例2 // 例5.2:学生成绩类的定义示例(续) float Student::average_mark( )
{ float total=0; for(int i=0;i<exams_taken;i++) total+=marks[i]; return total/exams_taken; } void Student::format( ) { cout<<setw(10)<<name<<",平均成绩:"<<setprecision(4) <<setw(6) <<average_mark(); void Student::Print( ) { cout<<"学生:"; format(); cout<<endl;
22
类和对象使用举例2 // 例5.4:学生成绩类对象的使用示例 #include <iostream.h>
#include "Student.h" void main() { float st_marks[ ][5]={{81,82,87,84,85}, {78,59,92,79,84},{90,100,89,85,92}}; char st_name[ ][10]={"李和平","张民主","白话"}; Student gs[3]; //定义对象数组 for(int i=0;i<3;i++) //操作每个对象数组元素 { gs[i].Setstudent(5,st_marks[i]); gs[i].name=st_name[i]; gs[i].Print(); }
23
5.4 对象的初始化-构造函数 1.构造函数 构造函数是类的特殊成员函数。主要用于为对象的数据成员分配存储空间和初始化。 2.构造函数的特点
1.构造函数 构造函数是类的特殊成员函数。主要用于为对象的数据成员分配存储空间和初始化。 2.构造函数的特点 构造函数不允许有任何返回类型,void也不能有; 构造函数名必须和本类类名相同; 构造函数的参数个数可以为0,也可以有多个,故构造函数可以重载; 构造函数应定义为公有成员;
24
3.关于构造函数的说明 若用户没有定义构造函数,编译程序将自动生成一个无参 的、函数体为空的构造函数。
若用户没有定义构造函数,编译程序将自动生成一个无参 的、函数体为空的构造函数。 无形参或形参都有缺省值的构造函数称为缺省构造函数。 构造函数由系统在创建对象时自动调用,调用时机为: 遇到对象定义时自动调用构造函数; 遇到 new 类名 或 new 类名[长度]时自动调用缺省构造函数;遇到 new 类名(实参表)时自动调用带参构造函数。 遇到 类名(实参表) 时自动调用构造函数。(匿名对象) 注意:定义对象指针和对象的引用时并不执行构造函数
25
练习:假定AB为一个类,类中定义了带一个构造函数。当程序中有如下语句时:
AB a(4),b[5],*p[2]; AB类的构造函数调用了几次? 答案:6次
26
例:为矩形类添加构造函数
27
4.定义对象的初始化参数问题 若类中没有定义构造函数,或只定义了无参的构造函数,定义该类对象时不可带参数。
28
4.定义对象的初始化参数问题 若类中只定义了带参的非缺省构造函数,在定义该类对象时必须带参数。
29
4.定义对象的初始化参数问题 若类中定义了有形参的缺省构造函数,在定义该类对象时参数可带可不带。
30
5.对象数组的初始化 6.对象的运算:未重载定义任何运算符的情况下,只能做取地址运算和赋值运算
31
5.4 对象的初始化-析构函数 1.析构函数 析构函数是类的特殊成员函数。主要用于释放对象占用 2.析构函数的特点 3.析构函数的调用时机
的存储空间及其他必要的处理工作。 2.析构函数的特点 析构函数不允许有任何返回类型,void也不能有; 析构函数名为本类类名前加一个“~”; 析构函数不能有参数,所以不能重载析构函数; 析构函数应定义为公有成员; 3.析构函数的调用时机 在对象生存期结束时自动执行。
32
带析构函数的日期类使用示例 生存期同时结束的多个对象的析构函数的调用顺序与它们的构造函数的调用顺序相反。
33
关于析构函数的说明: 若用户没有定义析构函数,编译程序将自动生成一个函数
若用户没有定义析构函数,编译程序将自动生成一个函数 体为空的析构函数。它可以释放除用new运算外为对象分配的所有空间。 如果用new运算符为对象的成员分配了空间,应该在类中定义析构函数,用delete释放申请的空间;否则,可能会出现内存泄漏。
34
例2:析构函数应用举例 程序运行结果: 对电视机调用构造函数 对洗衣机调用构造函数 对洗碗机调用构造函数 电视机 2350
洗衣机 1700 洗碗机 700 对洗碗机调用析构函数 对洗衣机调用析构函数 对电视机调用析构函数
35
问题1:在前例中可否不定义析构函数? 会产生动态内存泄漏 电 视 机 \0 name price p1 内存泄漏 2350
36
问题2:在前例中name可否不用动态内存分配 直接使用指针?
price p1 2350 ps TV Set\0
37
问题3:在前例中可否不用动态内存分配, 用字符数组来实现? ps name price p1 TV Set\0 2350
38
5.4 对象的初始化-拷贝构造函数 1.拷贝构造函数 2.拷贝构造函数的特点 拷贝构造函数是类的特殊成员函数。
作用是用一个已定义的对象初始化一个被创建的同类对象。 2.拷贝构造函数的特点 该函数不允许有任何返回类型; 该函数的名字必须和本类类名相同; 该函数的第一个参数必须是一个本类对象的引用,如果还有其它参数,那么它们都必须具有缺省的参数值。 注意:只掌握仅有一个参数是本类对象的引用的情况即可
39
关于拷贝构造函数的讨论: 若用户没有定义拷贝构造函数,编译程序将自动生成一个拷 贝构造函数,该函数把已定义对象的每个数据成员的值复制
若用户没有定义拷贝构造函数,编译程序将自动生成一个拷 贝构造函数,该函数把已定义对象的每个数据成员的值复制 给新创建对象的对应数据成员。 拷贝构造函数由系统自动调用,调用时机为: 用一个已定义的对象初始化一个新的同类对象; 在调用一个函数时,把对象作为函数实参传递给函数形参; 对象作为函数返回值。 注意:对象赋值时不调用拷贝构造函数; 由系统生成的拷贝构造函数可能会造成指针悬挂和内存泄漏。
40
拷贝构造函数举例(教材例5.12) 程序运行结果: 调用带参构造函数 调用缺省构造函数 调用拷贝构造函数 进入pf()函数 p1=(5,7)
41
5.4 对象的初始化-子对象的初始化 1.子对象:类的数据成员可以是一个已定义类的对象(不能是本类的对象),把这样的数据成员称为对象成员或子对象。 例:子对象举例。 class B{ … }; class A{ B bb; … }; //bb为子对象 2.含有子对象的类的构造函数 含子对象的类的构造函数形式为: 构造函数名(形参表):数据成员初始化表 {函数体} 数据成员初始化表的形式为: 子对象初始化表[,本类基本类型数据成员初始化表] 子对象初始化表的形式: 子对象名1(参数表),子对象名2(参数表),……
42
关于子对象初始化的讨论: 1. 当在程序中创建具有子对象的类的对象时,执行构造函数的顺序是:
(1)按类中各子对象的定义顺序调用它们各自所属类 的构造函数对各子对象初始化。 (2)执行对本类基本类型数据成员初始化。 (3)执行本类的构造函数体。 2. 当具有子对象的类的对象的生命期结束时,析构函数执行的顺序正好与构造函数相反
43
含子对象的类的构造函数举例 程序运行结果: B class(a=2,b=5) A class( x=0) B class(a=3,b=9)
m:x=0 a=2 b=5 n:x=7 a=3 b=9
44
5.5 this指针 this指针 是一个由系统定义的、隐含于类的每个成员函数中的特殊指针,它指向当前调用成员函数的对象(称为当前对象)。可在成员函数中使用。 include <iostream.h> class Test { int x; public : Test( int a=0 ) { x=a; } void Print( ); }; void Test::Print( ) { cout<<“x=“<<x<<endl; } void main( ) { Test t1(12),t2(20); t1.Print(); //t1为当前对象 t2.Print(); //t2为当前对象 } { cout<<“x=“<<this->x<<endl;}
45
1.编译系统在处理类定义时,将this指针作为每个成员函数的隐含参数。如:
//用户定义的成员函数 void Test :: Print( ) { cout << x << endl; } //编译器处理的成员函数 void Test :: Print( Test *this ) { cout << this->x << endl; } 2.通过对象调用成员函数时,系统将对象的地址作为隐含的实参传给this指针。如: t1.print( ); 改变为: print( &t1 ); t2.print( ); 改变为: print( &t2 );
46
this指 针 举 例 #include <iostream.h> class Test { int x; public :
Test & Setx( int a=0 ) { x=a; return *this; } Test & Print( ) { cout<<"x="<<x<<endl; return *this; } }; void main( ) { Test t1,t2; t1.Setx(5).Print( ); t2.Setx().Print().Setx(10).Print(); } this指 针 举 例 运行结果: x=5 x=0 x=10
47
5.6 其它定义类的形式 可以用关键字 struct 定义类,类体内容和格式与用class定义类完全相同。它们的差别仅在于: 前者的缺省访问权限是公有 (public),而后者的缺省访问权限是私有 (private)。 例:类的定义举例。矩形类的定义。 struct rect{ //成员函数为公有 void setXY(double x1,double y1); double getX( ); double getY( ); double area( ); private: double x,y; };
48
5.7 类的静态成员 类的静态成员 类的所有对象共享的成员。它提供了一种在对象间数据共享的方式。包括静态数据成员和静态成员函数。
1. 静态数据成员 是类中带有static修饰符的一个数据成员,使用时必须在任何类体和函数之外定义一次。 ✿ 静态数据成员的说明(类体内): static 类型名 静态数据成员名; //不能赋初值 ✿静态数据成员的定义(类体外): 类型 类名::静态数据成员; //缺省初始值为0 类型 类名::静态数据成员=初始值;
49
静态数据成员的使用
50
讨论:静态数据成员的特点 一个静态数据成员属于类的所有对象,即使没定义对象,它依然存在。 静态成员可以是私有也可以是公有。
对公有静态数据成员除通过对象访问外,在类定义范围之外还可用以下形式访问: 类名::公有静态数据成员
51
静态数据成员的使用:记录对象的个数
52
5.8 友元函数与友元类 1.友元函数 2.友元函数的说明 必须在类体中给出友元函数原型声明。
不属于本类的、可以访问本类对象的私有成员的函数。 类的友元函数只能通过该类的对象访问该类的私有成员。 类的友元函数可以是一个普通函数,也可以是其他类的一个成员函数 2.友元函数的说明 必须在类体中给出友元函数原型声明。 把普通函数说明为友元的格式为: friend 类型 函数名(形参表); 把其他类的成员函数说明为友元的格式为: friend 类型 类名:: 函数名(形参表);
53
例 :用友元函数计算两个复数的和。 程序运行结果: (2+1.5i)+(3-6i)=(5-4.5i)
54
5.8 友元函数与友元类 3.友元类(了解) 4.友元类的说明 在类体中,说明友元类的格式为: 举例:
当一个类为另一个类的友元类时,该类的所有成员函数均为另一个类的友元函数。 4.友元类的说明 在类体中,说明友元类的格式为: friend class 友元类名; 举例: Class b{ …. friend class a; } Class a{ …. } 友元的使用降低了数据的隐藏性,对它不能滥用。
55
5.9 类模板 1.类模板:一组类的通用描述 2.类模板的说明形式
一个类模板为类定义了一种通用模式,使类中的某些数据成员、某些成员函数的参数及局部变量或返回值能取任意类型。 2.类模板的说明形式 template <模板参数表> class 类名 { …… }; 模板参数的两种形式: 1) class 或 typename 标识符 这种形式的模版参数使用时用任意类型实例化 2) 类型名 标识符 这种形式的模版参数使用时用对应类型常量实例化
56
不同类型数组的类模板的说明与使用示例。 程序运行结果:
Similar presentations