《C程序设计《类和对象》.ppt》由会员分享,可在线阅读,更多相关《C程序设计《类和对象》.ppt(63页珍藏版)》请在三一办公上搜索。
1、C+程序设计,类和对象,第3章 类和对象,本章学习要点,类和对象的概念及其关系类和对象的定义及其使用构造函数与析构函数的作用及使用对象与指针对象与const对象的赋值和复制对象作为函数的参数学生信息管理系统中类和对象的实例,第3章 类和对象,本章学习目标,正确理解类和对象以及封装的概念熟练掌握类和对象的定义及其使用理解构造函数与析构函数的作用及使用掌握对象与指针及const的特点及使用理解并掌握对象的赋值和复制理解对象作为函数的参数传递的实质掌握基本的面向对象的设计与实现,3.1类的声明和对象的定义,面向对象的核心是对象系统是由一个一个对象构成的对象是由类实例化得到的程序员在程序设计时是针对类
2、进行的本节主要是关于类和对象的概念以及类的声明和对象的定义,3.1.1 类和对象的概念及关系,对象封装了数据及在这些数据之上的操作的封装体 类对具有相同属性和操作的一组对象的抽象描述 类和对象的关系 类是对象的抽象;对象是对类的实例化,类和对象的关系,3.1.2 类的声明,class 类名public:公用成员protected:受保护成员private:私有成员;,声明类的一般形式:,声明类的关键字,合法标识符,公用成员限定符,受保护成员限定符,私有成员限定符,类结束要加分号,3.1.2 类的声明,【例3-1】声明一个学生类,要求包括学生的学号、姓名、性别等信息,并且能够显示学生的信息。/=
3、class Student/声明Student类public:/以下部分为公用成员函数void Show()coutNo.:stuNoendl;coutName:stuNameendl;coutSex:stuSexendl;private:/以下部分是私有数据成员int stuNo;char stuName20;char stuSex;/类声明结束,此处必须有分号/=,3.1.2 类的声明,1.类的成员分为数据成员和成员函数2.成员的可访问性可分为三类:私有的(private)、受保护的(protected)、公用的(public)3.由访问限定符限定它后面的成员的访问属性,直到出现下一个访问
4、限定符或者类的结束为止4.在声明类时,这三种访问属性的成员的声明次序是任意的,并且在一个类的声明中不一定这三种访问属性全部都出现,可能只出现两种或一种。5.某个成员访问限定符在一个类的声明中也可以出现多次。,注意,3.1.3 对象的定义,声明类之后,再定义对象如:Student zhang,wang;或:class Student zhang,wang;在声明类的同时定义对象如:class Student zhang,wang;不出现类名,直接定义对象如:class zhang,wang;,声明类时系统并不分配内存单元,而定义对象时系统会给每个对象分配内存单元,3.2.1成员函数的性质,不属于
5、任何类的函数称为普通函数 成员函数隶属于某个类成员函数与普通函数的区别成员函数是属于某个类,定义在类的内部成员函数可以指定为访问属性成员函数可以访问本类的任何成员普通函数只能访问对象的公用成员对于成员函数,一般是将需要被类外调用的声明为公用的,不需要被类外调用的声明为私有的,3.2.1成员函数的性质,不属于任何类的函数称为普通函数 成员函数隶属于某个类成员函数与普通函数的区别成员函数是属于某个类,定义在类的内部成员函数可以指定为访问属性成员函数可以访问本类的任何成员普通函数只能访问对象的公用成员对于成员函数,一般是将需要被类外调用的声明为公用的,不需要被类外调用的声明为私有的,3.2.2在类外
6、定义成员函数,【例3-4】将Student类的成员函数改为在类外定义的形式。/=class Student/声明Student类类型public:/以下部分为公用成员函数void Show();private:/以下部分是私有数据成员int stuNo;char stuName20;char stuSex;/类声明结束void Student:Show()/在类的声明之外定义成员函数coutNo.:stuNoendl;coutName:stuNameendl;coutSex:stuSexendl;/=,3.2.2在类外定义成员函数,(1)成员函数在类内定义或是在类外定义,对程序执行的效果基本一
7、样。(2)在类外定义成员函数时,必须首先在类内写出成员函数的原型,然后再在类外定义。(3)如果在类外有函数定义,但是在函数名前没有类名和作用域运算符进行限定,则该函数被认为是普通函数。(4)如成员函数的函数体很短,也可以将其定义在类内。(5)在类内声明成员函数,在类外定义成员函数,是软件工程中要求的良好的编程风格。,注意,3.2.3 inline成员函数,inline函数在编译时将被调用函数的代码直接嵌入到调用函数处 inline成员函数就是将类中的成员函数声明为内置的 当类中的成员函数是在类内定义时,默认该成员函数是inline成员函数 如果成员函数定义在类的外部,则在成员函数声明或定义前必
8、须要有inline关键字,3.2.4 成员函数的存储方式,实例化对象时需要分配内存空间,数据和函数都需要存储空间同一个类的不同对象的数据是不一样的,因此要为每个对象的数据成员分配内存单元同一个类的不同对象的函数是一样的,因此将同类的所有对象的函数放在一个公共的区域。,3.2.4 成员函数的存储方式,【例3-5】类的对象占用内存空间实验。/=#include using namespace std;class Testpublic:void Show()coutchar in Test is:cendl;private:char c;int main()Test test;coutSize of
9、 Test is sizeof(test)endl;return 0;/=,3.2.4 成员函数的存储方式,【例3-6】相同类的不同对象执行相同成员函数输出不同结果。/=#include using namespace std;class Testpublic:void Set(char ch)c=ch;void Show()coutchar in Test is:cendl;private:char c;int main()Test test1,test2;test1.Set(a);test2.Set(b);test1.Show();test2.Show();return 0;/=,3.3
10、对象成员的访问,使用对象时经常需要使用对象中的公用成员,即对象成员的访问 访问对象的成员主要有3种方法 通过对象名和成员运算符访问对象中的成员通过指向对象的指针访问对象中的成员通过对象的引用访问对象中的成员,3.3.1 成员运算符访问成员,通过对象名和成员运算符访问对象中的成员的一般形式对象名.成员名在使用对象的成员时一定要写清楚成员所属的对象,如果只写成员名则系统会误认为是一个普通的变量或函数 如:在【例3-6】中main函数里的“test1.Show();”语句,3.3.2 通过指针访问成员,通过指向对象的指针访问对象中的成员可以通过C+的“-”运算符方便直观的进行,“-”称为指向运算符。
11、,例如:Test test1;test1.Set(a);Test*pTest=,此处两个语句的执行结果完全相同,只是采用了不同的访问对象成员的形式而已。,3.3.3 通过引用访问成员,对象的引用和普通变量的引用在本质上是一样的 通过对象的引用访问对象成员和通过对象访问成员形式上是一样的,例如:Test test1;test1.Set(a);Test,此处两个语句的执行结果完全相同,只是采用了不同的访问对象成员的形式而已。,3.4.1构造函数,构造函数的作用创建对象时对对象的数据成员初始化 构造函数的特点构造函数与类名相同,且没有返回值 构造函数不需要用户调用,由系统在创建对象时自动调用的 构造
12、函数内容一般是初始化数据语句,但也可以是其他的语句创建对象时肯定会执行一个构造函数,3.4.1构造函数,带参数的构造函数 一般格式为:构造函数名(参数表);实参是在定义对象时给出的,一般格式为:类名 对象名(实参表);带参数的构造函数形式可以方便的实现对不同对象进行不同的初始化,3.4.1构造函数,【例3-10】定义两个长方体,分别求出它们的体积。这两个长方体的长宽高分别是4,2,3和5,1,2。#include using namespace std;class Boxpublic:Box(float L,float w,float h)/带有参数的构造函数 length=L;width=w
13、;height=h;float Volume()return length*width*height;private:float length,width,height;,int main()Box box1(4,2,3);/创建对象时给出实参Box box2(5,1,2);coutVolume of box1 is box1.Volume()endl;coutVolume of box2 is box2.Volume()endl;return 0;,3.4.1构造函数,构造函数与参数初始化表 这种方法不在构造函数的函数体内对数据成员初始化,而是在函数的首部实现,例如:class Boxpub
14、lic:Box(float L,float w,float h):length(L),width(w),height(h)float Volume()return length*width*height;private:float length,width,height;,使用参数初始化表的构造函数,3.4.1构造函数,构造函数重载 在类中定义多个构造函数,这些构造函数具有相同的函数名而参数表中参数的个数或类型不同相当于给类实例化对象时提供了不同的初始化方法,3.4.1构造函数,【例3-12】构造函数重载。/=#include using namespace std;class Boxpubl
15、ic:Box()/无参数的构造函数 length=1;width=1;height=1;Box(float L,float w,float h)/带有参数的构造函数 length=L;width=w;height=h;float Volume()return length*width*height;private:float length,width,height;/=,int main()Box box1(4,2,3);/调用带有三个参数的构造函数创建对象Box box2;/调用没有参数的构造函数创建对象coutVolume of box1 is box1.Volume()endl;cout
16、Volume of box2 is box2.Volume()endl;return 0;,3.4.1构造函数,(1)如果在类的声明中没有写一个构造函数,系统会自动生成一个无参的、函数体为空的默认构造函数。默认构造函数只是在形式上保证创建对象时必须有构造函数。(2)在程序中定义对象时一定要注意类中有几个构造函数,它们要求的参数分别是什么样的,如果创建对象时给出的参数表和所有的构造函数的参数表都不符合,则系统无法创建对象。(3)使用参数实例化对象时的格式是:类名 对象名(实参表);。而使用默认构造函数实例化对象时的格式是:类名 对象名;,注意,3.4.2 析构函数,析构函数的作用在系统释放对象之
17、前进行清理工作。析构函数的特点析构函数的函数名是固定的,由“”+“类名”组成析构函数没有返回值析构函数没有参数如果没有自己写出析构函数,系统会自动生成一个析构函数,3.4.2 析构函数,析构函数的特点析构函数在对象生命周期结束时由系统自动调用,析构函数的作用不是释放对象,释放对象是由系统来进行的,而是在系统释放对象之前进行一些清理工作,把对象所占用的额外内存空间归还给系统,注意,3.4.2 析构函数,【例3-13】带有析构函数的长方体类。/=class Boxpublic:Box()/无参数的构造函数 length=1;width=1;height=1;Box(float L,float w,
18、float h)/带有参数的构造函数 length=L;width=w;height=h;float Volume()return length*width*height;Box()/析构函数 coutDestructor of Box is called!endl;private:float length,width,height;/=,int main()Box box1(4,2,3);/调用带有3个参数的构造函数创建对象Box box2;/调用没有参数的构造函数创建对象coutVolume of box1 is box1.Volume()endl;coutVolume of box2 i
19、s box2.Volume()endl;return 0;,程序执行后运行的结果如下:Volume of box1 is 24Volume of box2 is 1Destructor of Box is called!Destructor of Box is called!,3.4.3 调用次序,总的原则当创建对象时调用构造函数,当释放对象时调用析构函数 创建对象是当程序执行到了非静态对象的定义语句或第一次执行到静态对象的定义语句释放对象则是对象到了生命周期的最后时系统释放对象或通过delete运算符动态释放new运算符动态申请的对象,【例3-14】验证调用构造函数和析构函数的顺序的例子,3
20、.5 对象数组,对象数组和普通的数组没有本质的区别,只不过普通的数组的元素是简单变量,而对象数组的元素是对象而已 对象数组在实际中主要用于系统需要一个类的多个对象的情况 在建立数组时,系统会自动调用每一个对象元素的构造函数(参见【例3-15】),3.5 对象数组,对象数组在建立时还可以给出实参以实现对数组元素进行不同的初始化 类有只有一个参数的构造函数,可以使用如下形式的数组初始化形式:Student students3=100,200,300;如果对象的构造函数需要多个参数,则在初始化的花括号里要分别写明构造函数,并指定实参 对象数组初始化参见【例3-16】,3.6 对象与指针,以指针的概念
21、为基础,同时又加入了对象的特点 对象与指针主要从三个方面进行讨论指向对象的指针指向对象成员的指针this指针,3.6.1 指向对象的指针,指针指向的是内存中对象所占用空间的首地址定义指向对象的指针的一般形式是:类名*指针名;通过指向对象的指针访问对象的成员有两种形式通过“-”调用对象的公用成员函数 通过“*”运算符得到对象,然后再使用“.”运算符调用对象的公用成员函数,3.6.2 指向对象成员的指针,指向对象数据成员的指针 指向对象数据成员的指针和普通的指针是完全相同的 指向对象成员函数的指针指向对象成员函数的指针需要在指针名前面加上所属的类名及域运算符“:”指向对象成员函数的指针不但要匹配将
22、要指向函数的参数类型、个数和返回值类型,还要匹配将要指向函数所属的类,3.6.2 指向对象成员的指针,【例3-17】使用指向对象成员函数的指针调用对象的成员函数/=int main()Box box(2,2,2);/创建Box的对象boxfloat(Box:*p)();/定义指向Box类的成员函数的指针pp=/=,(1)在给指向对象成员函数的指针进行赋值时要把类的函数名赋值给指针,而不是对象的函数名(2)调用指向对象成员函数的指针指向的成员函数时,要通过(对象名.*指针名)(实参表)的形式,而不是(类名.*指针名)(实参表)的形式。(3)定义指向对象成员函数的指针时可以同时进行初始化操作,形式
23、为:返回值类型(类名:*指针名)(形参表)=,注意,3.6.3 this指针,在每一个成员函数中都包含了一个特殊指针,这个指针的名字是固定的,称为this指针如Box类中的Volume函数,其中的height*width*length实际上是(this-height)*(this-width)*(this-length)this指针是指向本类对象的指针,它的指向是被调用成员函数所在的对象this指针是由系统通过参数隐式传递给成员函数的,this指针的处理过程,源程序,编译后,调用时,float Box:Volume()return length*width*height;,float Box:
24、Volume(Box*this)return this-length*this-width*this-height;,box1.Volume(,3.7 对象与const,在程序设计的过程中需要考虑的一个非常重要的因素就是数据的安全性在程序中,不同的模块之间经常出现需要共享数据的情况,此时数据的安全性降低在程序中既要让数据在一定范围内共享,又要保证数据的安全,这时可以使用const,把对象或对象相关成员定义成常量本节主要从常对象、常对象成员、指向对象的常指针、指向常对象的指针和对象的常引用五个方面进行说明,3.7.1 常对象,常对象中的所有成员的值都不能被修改常对象中数据成员为常变量且必须有初值
25、声明常对象的一般形式const 类名 对象名(实参表);类名 const 对象名(实参表);如果一个对象被声明为常对象,则不能调用该对象的非const型的成员函数(构造函数和析构函数除外)在成员函数声明的后面加上const即可访问,3.7.2 常对象成员,常数据成员常数据成员的声明和作用与普通的常变量类似在程序运行过程中常数据成员的值不能修改常变量在声明的同时必须初始化注意常数据成员在初始化时必须使用构造函数的参数初始化表,Box:Box(float L,float w,float h)length=L;width=w;height=h;,不能使用这种形式进行初始化,Box:Box(float
26、 L,float w,float h):length(L)width=w;height=h;,3.7.2 常对象成员,常成员函数就是const型成员函数如果在常成员函数中出现了修改数据成员的语句,系统编译是通不过的声明常成员函数的一般形式:返回值类型 成员函数名(形参表)const;,3.7.2 常对象成员,(1)在一个类中可以根据需要将部分数据成员声明为常数据成员,另一部分数据成员为非const数据成员(2)如果一个类的所有数据成员都不允许修改,可以将这个类中的所有数据成员都声明成const数据成员,或者定义对象时声明为const对象。(3)常对象中的数据成员都是const数据成员,但是常对
27、象中的成员函数不一定都是const成员函数(4)如果是常对象,只能调用其中的const成员函数(5)在类的定义中,常成员函数不能调用非常成员函数,注意,3.7.3指向对象的常指针,将指向对象的指针变量声明为const型定义指向对象的常指针的一般形式:类名*const 指针名=在给pbox赋初值后pbox的值不能再修改一般情况下指向对象的const指针用作函数的形参指针在函数的执行过程中不会改变指针的指向可以防止误操作,增加系统的安全性,3.7.4指向常对象的指针变量,指向常对象的指针变量和指向常变量的指针变量的概念和用法非常接近定义指向常对象的指针变量的一般形式:const 类名*指针变量名;
28、说明:如果一个对象已被声明为常对象,只能用指向常对象的指针变量指向它,而不能用一般的(指向非const型对象的)指针变量去指向它如果定义了一个指向常对象的指针变量,并使它指向一个非const的对象,则其指向的对象是不能通过指针来改变的,3.7.4指向常对象的指针变量,说明:如果定义了一个指向常对象的指针变量,虽然不能通过它改变所指向的对象的值,但是指针变量本身的值是可以改变的指向常对象的指针变量最常用于函数的形参,目的是在保护形参指针所指向的对象,使它在函数执行过程中不被修改 当希望在调用函数时参数对象的值不被修改,就应当把形参定义为指向常对象的指针变量,同时用对象的地址作实参,而对象参数可以
29、是const或非const型。,3.7.5 对象的常引用,对象的常引用表示一个对象的别名,但是无法通过引用来修改对象的数据成员声明对象的常引用的一般形式:const 类名 常引用的应用也是主要用在函数的形参中,保证函数调用时实参对象的安全性,例如:Clock clock(12,12,12);const Clock,若Display函数是const成员函数,则合法;若Display函数是非const成员函数,则调用是非法的。,3.8对象的动态创建和释放,使用new和delete运算符实现对对象的动态创建与释放 在程序执行到new语句时,系统会从内存堆中分配一块内存空间,存放类的对象,调用构造函数
30、初始化对象。如果内存分配成功,返回分配的内存的首地址;如果分配内存失败,则会返回一个NULL。,3.8对象的动态创建和释放,但是通过new运算符动态创建的对象没有名字,要声明一个指针变量来保存对象的首地址,如:Box*pc=new Box;,3.8对象的动态创建和释放,可以在使用new运算符创建对象时给出实参,调用带有参数的构造函数初始化对象如:Box*pc=new Box(2,2,2);为了保险起见,在使用对象指针之前一般先判断指针的值是否为NULL。如下所示:Box*pc=new Box(2,2,2);if(pc!=NULL)pc-Volume();,3.8对象的动态创建和释放,使用del
31、ete运算符释放该对象 delete运算符的使用格式是:delete 指针名;通过new运算符动态创建的对象只能通过delete运算符动态的释放,3.9对象的赋值和复制,对象赋值的一般形式对象名1=对象名2;对象赋值就是把对象2的数据成员的值复制给对象1对应的数据成员 这个操作通过对赋值运算符的重载来实现 默认的赋值运算符重载函数,Box,3.9.1对象的赋值,(1)同类对象之间的赋值操作只对其中的数据成员赋值,而不对成员函数赋值(2)如果类的数据成员中包含动态分配的数据,则不能使用默认的赋值运算符重载函数,必须亲自去写赋值运算符重载函数,否则就会引起指针悬挂问题。,注意,【例3-19】默认赋
32、值运算符重载函数引起的指针悬挂问题。,3.9.2对象的复制,在创建对象时使用已有对象快速复制出完全相同的对象对象复制的一般形式类名 对象2(对象1);类名 对象2=对象1;复制构造函数只有一个形参,这个形参就是本类对象的引用复制构造函数的代码主要是将形参中对象引用的各数据成员值赋给自己的数据成员,3.9.2对象的复制,以Box类为例,看看复制构造函数的形式:Box:Box(const Box,普通构造函数和复制构造函数的区别:(1)在形式上普通构造函数一般是形参列表而复制构造函数的形参则只有一个(2)在调用时系统会根据实参类型来自动的选择调用普通构造函数还是复制构造函数。(3)调用的情况不同,
33、普通构造函数是在创建对象时由系统自动调用;而复制构造函数是在使用已有对象复制一个新对象时由系统自动调用,在以下3中情况下需要复制对象:程序中需要新创建一个对象,并用另一个同类的对象对它初始化 函数的参数是类的对象,函数的返回值是类的对象。,3.9.2对象的复制,在没有涉及指针类型的数据成员时,默认复制构造函数能够很好地工作。当一个类有指针类型的数据成员时,默认复制构造函数常会产生指针悬挂问题。,3.9.3对象的赋值与复制的比较,相同点主要有:(1)对象的赋值和复制大部分情况下都是把一个对象的数据成员依次赋给另外一个同类对象的相应数据成员。(2)如果不重载赋值运算符或不提供复制构造函数,系统都可
34、以提供缺省的代码。(3)系统会根据情况自动的调用对象的赋值运算符重载函数或对象的复制构造函数。,不同点主要有:(1)对象的赋值是在两个对象都已经创建的基础上进行的;而对象的复制则在用一个已有对象复制一个新对象时进行的。(2)它们两个所对应调用的函数不同,对象的赋值系统调用的是赋值运算符重载函数;而对象的复制系统调用的是复制构造函数。,3.10向函数传递对象,向函数传递对象分为值传递、地址传递和引用传递三种 在值传递中,系统会自动地调用复制构造函数按照实参的“样子”以形参的名字为对象名创建局部对象,在函数内部就使用这个与实参对象相同的局部对象。地址传递的方式是将实参的地址传递给形参,系统并没有再创建和实参一样的局部对象引用传递的方式是将实参的名字传递给形参,使形参成为实参的别名,系统并没有再创建和实参一样的局部对象,在函数中访问的对象就是实参对象。,3.11学生信息管理系统实例,按照C+规定的声明类的格式对系统中的日期类进行声明。日期类的声明代码放在名为date.h的头文件中Date类各成员函数的类外实现代码放在名为date.cpp的源文件中可以在main函数中创建Date类的对象进行验证,Thank You!,