C类定义及其应用.docx
- 文档编号:17417717
- 上传时间:2023-07-25
- 格式:DOCX
- 页数:29
- 大小:36.75KB
C类定义及其应用.docx
《C类定义及其应用.docx》由会员分享,可在线阅读,更多相关《C类定义及其应用.docx(29页珍藏版)》请在冰点文库上搜索。
C类定义及其应用
准备知识:
⑴命名空间(namespace)
一个软件往往由多个模块组成,其中会包括由不同程序员开发的组件以及类库提供的组件,因此,在对标识符命名时有可能发生冲突。
一个命名空间将不同的标识符集合在一个命名的作用域内,以防止命名冲突。
比如以如下形式声明一个命名空间s:
namespaces{
classA{};
voidfun();
}
则引用标识符的方式如下:
s:
:
Aa;
s:
:
fun();
即在标识符前加命名空间名称及“:
:
”的前缀。
E1_1
如果使用声明:
usingnamespace<已命名的命名空间名称>;
比如:
usingnamespaces;
该命令用来打开命名空间的限制,则该命名空间中所有的标识符在当前作用域中都可以直接使用,不需要任何前缀。
E1_2
在C++标准程序库中,使用了命名空间std,所有标识符都声明在命名空间std中。
在使用C++标准程序库中的任何标识符时,可以直接指定标识符所属的命名空间,例如:
std:
:
cout,也可以使用using命令来打开命名空间的限制。
C++标准程序库中中的头文件不再有“.h”的扩展名,因此,在程序开始处使用#include
E1_3
在VisualC++6.0编译环境中要使用一系列的I/O流类,就应该包含头文件iostream。
⑵对象:
现实世界中的一切事物都是对象,对象可以是有形的,比如一间房间,一本书籍;也可以是无形的,比如一个计划。
对象可以是一个简单的个体,比如一个学生;也可以是由其它对象组合而成,比如一个公司有多个部门,每个部门又由许多员工组成。
对类似的对象进行抽象,找出共同的属性就可以构成一种类形。
作为面向对象的程序设计语言,C++支持“抽象”。
将抽象后的数据和函数“封装”在一起,就构成了C++的“类”。
⑶在面向过程的结构化程序设计中,程序的模块是由函数构成的。
函数将逻辑上相关的语句和数据封装,用于完成特定的功能。
在面向对象的程序设计中,程序的模块是由类构成的。
类是对逻辑上相关的函数和数据的封装,是对问题的抽象描述。
即将抽象得到的数据和函数有机的结合成一个整体,形成“类”。
其中的数据和函数都是类的成员。
类实际上相当于一种用户自定义的类型,在定义一个类时要说明其数据和操作内容。
就象一个钟表,是“钟类”的一个实例。
它调节时间的装置是“外部接口”,同时,其内部的各个部件有机的进行工作,但是外界是不能干预的。
类的特性:
封装、继承和多态。
类的定义:
class类名称标识符{
成员列表:
包括数据成员和成员函数的定义及存取控制类别。
};
类成员函数的实现
其中:
成员列表:
数据成员(属性),成员函数(行为方法)。
成员的存取控制类别:
private:
除了友元函数外,类的外界(对象)不能直接访问该类别成员,只有类定义中的成员函数或数据成员可以访问该类别成员。
在类定义中private:
若紧接著类名称,该关键字可以省略。
※很好的体现了类的封装特性。
public:
类的外界(对象)可以直接访问该类别成员,并且可以通过该类别成员访问private类别的类成员。
※定义了类的外部接口。
protected:
在涉及类继承时使用。
※类成员的存取控制类别定义没有先后顺序。
作用域说明符:
:
:
用于说明类成员的归属。
比如:
类定义中的成员函数的定义在类定义中完成,而成员函数的实现通常是在类定义外完成的。
所以,要使用作用域说明符来说明成员函数归属于那一个类。
当然,类定义中的成员函数的实现也可以在类定义中完成,但是,这样的函数通常只包含简单的顺序结构的语句(见内联函数)。
类的使用:
定义类的对象(类的对象是类定义的实例)。
类对象的建立(分配存储空间)需要构造,释放内存空间需要析构。
构造函数:
在创建对象时,利用特定值将对象初始化为一个特定的状态。
构造函数在对象被创建时被自动调用,没有函数返回值。
在类的public成员中可以定义有多个参数数量及参数类型不同的构造函数,函数名称即为类的名称。
YSC1-1、YSC1-2
构造函数执行步骤:
⑴按照初始化列表为相应属性赋初值,如果成员为对象,按照给定的初始值执行该对象的构造函数。
⑵执行构造函数的函数体语句。
YSC1-3、YSC1-4
※若在类的定义中没有定义构造函数,C++编译器将自动产生缺省的构造函数。
该函数无参,也不做任何实质性的工作。
因为在对象被创建时自动调用构造函数是“例行公事”。
析构函数:
用于完成对象生存期结束前的一些清理工作。
函数调用结束,对象在内存中所占内存空间被释放。
析构函数在对象的生存期即将结束时被自动调用,没有参数和函数返回值。
在类的public成员中可以定义一个(只能是一个)析构函数,函数名称为在类的名称前加符号“~”。
YSC1-1、YSC1-2
析构函数执行步骤:
若程序中有多个类对象存在,对象析构时的顺序与构造的顺序相反。
YSC1-5
※若在类的定义中没有定义析构函数,C++编译器将自动产生缺省的析构函数。
该函数无参,也不做任何实质性的工作。
因为在对象被创建时自动调用析构函数同样是“例行公事”。
※构造函数和析构函数无函数返回值,所以,该函数也无须定义函数类型。
※若有动态分配内存的操作,定义构造函数和析构函数是十分必要的
还可以根据需要在类定义中定义拷贝构造函数和赋值函数。
拷贝构造函数:
特殊的构造函数。
调用拷贝构造函数的情况:
⑴当用已经存在(构造完毕)的同类对象(初始值对象)的引用作为参数,初始化构造新建立的对象时将调用拷贝构造函数。
函数是在构造新的对象时被调用执行的。
函数的参数必须是同类对象的引用。
YSC1-6-1
YSC1-6-1程序中Ak(h);语句调用A(constA&a){x=a.x+2;y=a.y+4;}函数的执行。
const表示不能在函数语句中修改参数对象。
⑵如果函数的形参是类的对象,调用函数时,实参给形参参数传递赋值时将调用拷贝构造函数。
YSC1-6-2
⑶如果函数的返回值是类的对象,函数执行结束完成返回时将调用拷贝构造函数。
YSC1-6-3
YSC1-6-3程序中k对象是函数f()中的局部对象,函数f()执行结束就不存在了。
编译系统会在主调函数的主函数中创建一个“临时无名对象”,该临时对象的生存期只在调用语句的表达式h=f()中。
执行returnk;语句时,实际上是调用拷贝构造函数将k的值拷贝到临时对象中。
函数f()执行结束时k对象释放(注意:
该对象要析构),但是,临时对象存在于表达式h=f()中。
计算完该表达式后,临时对象的工作也就完成而被释放了(注意:
该临时对象要析构)。
⑷如果被拷贝的对象本体与参数对象的实体是一致的,则无须定义拷贝构造函数,利用编译系统生成的缺省函数即可完成拷贝构造(浅拷贝)。
而被拷贝的对象本体与参数对象的实体是不一致的,则必须定义拷贝构造函数(深拷贝)。
YSC1-7
YSC1-7程序中类定义中包含指针成员,申请动态内存空间,应在类中定义拷贝构造函数,在拷贝数据时将调用拷贝构造函数。
这样可以避免产生“两次析构”同一数据的错误产生。
※若在类的定义中没有定义拷贝构造函数,C++编译器将自动产生缺省的拷贝构造函数。
该函数的功能是把初始值对象的每个数据成员的值等值赋值给新建立对象的数据成员。
类的组合:
类的组合描述的是类定义中嵌有其它类的对象作为成员的情况,这是一种包含和被包含的关系。
当创建类的对象时,如果该类具有内嵌对象成员,则各内嵌对象将首先被自动创建。
因此,在创建对象(组合类的对象)时,既要对本类的基本类型的数据进行初始化,又要对内嵌对象的成员进行初始化。
在创建一个组合类的对象时,不仅其自身的构造函数将被调用,而且,还将调用其内嵌对象的构造函数,构造函数的调用顺序是:
⑴按照组合类中内嵌对象的书写顺序调用内嵌对象的构造函数。
⑵执行组合类构造函数的函数体。
如果定义组合类对象时未指定对象的初值,则默认形式(无形参)的构造函数被调用,此时内嵌对象的默认形式构造函数也被调用。
析构函数的调用执行顺序与构造的调用执行顺序相反。
如果定义组合类时未定义拷贝构造函数,则C++编译器将自动产生缺省的拷贝构造函数,且调用缺省的拷贝构造函数时系统将调用内嵌对象成员的拷贝构造函数。
如果定义组合类时定义拷贝构造函数,则需要为内嵌对象成员的拷贝构造函数传递参数。
YSC1-8
赋值函数:
用已经存在(构造完毕)的同类对象的引用作为参数,向已经存在(构造完毕)的同类对象做赋值操作时将调用赋值函数。
该函数实际上是对“=”的操作符重载,尤其当类中含有指针成员时,必须对对象的赋值操作定义赋值函数。
YSC1-9、YSC1-10
YSC1-9程序中h=g;语句调用A&operator=(constA&a)函数的执行。
const表示不能在函数语句中修改参数对象。
h对象是调用该函数的对象,g对象是该函数的参数对象。
该函数作为类的成员函数形式定义,必须有一个返回值。
由于,函数定义时将函数定义为对象的引用(&),所以,语句return*this;返回h对象的引用值。
该函数中的this指针是个隐含于每个成员函数中的特殊指针,他指向正在被该函数操作的对象,在YSC1-9程序中为对象h。
YSC1-11
YSC1-11程序中的语句if(this==&s)return*this;是在检查是否为自赋值。
语句delete[]p;先释放指针变量p原指向的内存单元,然后在后续语句中重新申请分配内存。
※若在类的定义中没有定义赋值函数,C++编译器将自动产生缺省的赋值函数。
该函数的功能是把表达式赋值号右侧对象的每个数据成员的值等值赋值给赋值号左侧对象的数据成员(这样做要慎重!
)。
内联函数:
类定义中成员函数可以定义成内联函数,方法是在函数定义前加关键字inline。
在普通函数调用时将程序转到被调函数的内存地址中,结束后再返回,这样做存在有时间及空间上的开销。
函数内联是将函数体在编译时来替换调用函数的语句,也就没有“转来转去”的开销了,但是这样做增加了程序目标代码量。
因此,内联函数是为解决函数调用的效率问题。
在内联函数中不允许包含循环和开关语句,内联函数定义必须出现在第一次被调用之前。
通常内联函数中只包括简单的顺序结构语句。
并且,编译系统将成员函数的实现在类定义中完成的函数也看成是内联函数。
静态数据成员:
某一类的所有对象具有相同的属性(属性的数量、名称及数据类型相同),但各个对象的具体属性值各不相同,这样的属性可以称为对象属性(实例属性)。
这样的属性(数据成员)默认的存储类型属于动态型。
类属性:
是描述类的所有对象共同特征的数据项,对于任何对象实例,该属性值是相同的,即该属性值为整个类共有,不属于任何一个具体的队象。
可以用static关键字声明成员为静态数据成员。
静态数据成员属于类,被类对象所共有,该类对象均可以访问该静态数据成员。
※静态成员的赋初值在类外进行,只能通过类名对其进行访问。
静态成员函数:
用static关键字声明的成员函数也属于整个类,由同类的所有对象共有。
对于public权限的静态成员函数可以通过类名或对象名来调用。
YSC2
补充:
E3_1、E3_2、E3_3
思考题:
输入一组整数,找出并显示最大值、最小值;显示类加和及平均值。
友元函数:
由于类的成员函数是以对象为中心的,必须通过对象才能调用相应的成员函数,并且,某类对象的成员函数不能直接访问其它类对象的private成员。
但是,将类A的成员函数声明为类B的友元函数,类A的对象就可以通过该友元函数直接访问类B的对象的private成员了。
如上所述,对于类B而言,它的友元函数不是其成员函数,但是,它的友元函数需要在其类定义中说明,并在函数说明语句前加关键字friend。
YSC3-1
YSC3-1程序中Teacher类的成员函数voids_g(Student&s)在Student类中被定义成该类的友元函数:
friendvoidTeacher:
:
s_g(Student&s);
如果类A是类B的友元类,则类A的所有成员函数都是类B的友元函数,都可以访问类B的private成员。
YSC3-2
YSC3-2程序中Teacher类在Student类中被定义成该类的友元类:
friendclassTeacher;,Teacher类的成员函数均是Student类的友元函数。
友元的特点:
⑴友元关系不能传递,即如果类B是类A的友元,类C是类B的友元,但没有其它声明,则类A和类C之间没有友元关系。
⑵友元关系是单向的,即如果类B是类A的友元,则类B的成员函数可以访问类A的private成员,但反之不可以。
⑶友元关系不能被继承,即如果类B是类A的友元,则类B的派生类并不会自动成为类A的友元。
指针:
可以定义指向类的成员及指向类对象的指针变量。
对象指针变量:
用于存放对象指针的指针变量。
对象指针变量在使用之前要先定义,并应进行初始化赋值,让该指针变量明确指向一个已经定义过的对象。
通过对象的指针变量可以访问到对象的public成员。
this指针变量:
是隐含于每个类的成员函数中的特殊指针变量。
this指针变量用于指向正在被成员函数操作的对象。
指向类的非静态成员的指针变量:
指向对象成员的指针变量在使用之前要先定义,说明该指针变量可以指向对象所在类的的成员,并应进行初始化赋值,让该指针变量明确指向类的哪一个成员。
通过该指针变量只能访问到对象的public成员。
由于类的定义中只是确定了各个数据成员的数据类型及在类中的相对位置,并不为数据成员分配具体的内存空间。
因此,在定义了指向对象成员的指针变量后,只是说明了被赋值的指针变量是专门用于指向哪个数据成员的,同时在该指针变量中存放该数据成员在类中的相对位置。
在定义了类的对象后,只要将对象的指针与指针变量中存放的相对位置结合起来就可以访问对象的数据成员了。
指向类的静态成员的指针变量:
对类的静态成员的访问是不用依赖于对象的,因此,可以用普通的指针变量来指向和访问静态成员。
YSC4
YSC4程序中的intA:
:
*p=&A:
:
c;语句定义指针变量p指向类A的数据成员c。
YSC4程序中的int(A:
:
*q)(int)=&A:
:
f;语句定义指针变量q指向类A的成员函数f。
YSC4程序中的A*k1=&x;语句定义指针变量k指向类A的对象x。
因为程序运行时应该通过定义对象来访问类的成员,所以,如上所用指针变量,需要先定义类A的对象,然后通过对象来使用上述指针变量。
如YSC4程序中的语句
x.*p=6;使得对象x的成员c的值为6。
k1->*p=9;使得对象x的成员c的值为9。
k2->c=1;使得对象y的成员c的值为1。
cout<<"(k1->*q)(5)="<<(k1->*q)(5)< 继承与派生: 继承机制是利用已有的数据类型来定义新的数据类型,使得新定义的数据类型不仅拥有自定义的成员,同时还“有条件”的拥有“旧有”的成员,从而实现代码的重用和扩充。 若类B继承类A,则称类B是类A的派生类(子类),类A是类B的基类(父类)。 可以在基类中抽象定义各种方法,而在派生类中增加扩充和改进的方法和属性。 继承的种类: 一个派生类可以有一个或多个基类。 只有一个基类时称为单继承;有多个基类时称为多继承。 继承的特点: ⑴继承关系可以是多级的,如类Y继承类X,类Z继承类Y。 ⑵不允许循环继承。 ⑶继承是单向的。 继承的结果: 派生类对象包含两部分数据: 继承自基类的数据部分(除了构造函数和析构函数以外的所有成员,实现了代码的重用。 )和派生类自身定义的数据部分(实现了代码的扩充)。 另外,还可以在派生类中定义和基类中同名的成员,从而达到对基类成员的隐藏控制。 YSC5-1、YSC5-2 YSC5-2程序中的类B继承类A,B类对象g可以使用的方法除了类B中定义的函数f2以外,还可以使用类A中定义的函数f1。 YSC5-2程序中并未定义A类对象。 如果定义A类对象,该对象所占内存单元为一个整型数据单元(以VisualC++编译系统为例是4个字节);因为类B继承类A,所以,B类对象g所占内存单元中分为两个部分: 继承自基类的数据部分和派生类自身定义的数据部分(以VisualC++编译系统为例是8个字节)。 派生类的构造函数: 基类的构造函数不能被继承。 在派生类中,如果对派生类新增的成员进行初始化,就必须要为派生类编写新的构造函数。 派生类的构造函数需要以恰当的初值数据作为参数,其中一些参数要用于对派生类自身新增加的成员进行初始化,另一些参数则要传递给基类的构造函数,用于初始化相应的成员。 注意: 如果基类定义了带有形参表的构造函数时,派生类就应当定义构造函数。 派生类对象的构造顺序: 先构造继承数据部分,再构造自身数据部分。 第⑴步骤: 首先按照派生类对象有无参数调用派生类相应的构造函数。 第⑵步骤: a)若该构造函数有初始化数据列表 ①若该初始化数据列表中对继承数据部分和自身数据部分的初始化内容都存在,则不论书写顺序怎样,先调用基类的相应构造函数构造继承数据部分,然后再依次初始化自身各个部分的数据。 YSC5-3-1 ②若该初始化数据列表中只有继承数据部分的初始化内容,则对继承数据部分调用基类中相应的构造函数完成初始化。 YSC5-3-2 ③若该初始化数据列表中只有自身数据部分的初始化内容,则先调用基类中的无参构造函数完成继承数据部分的初始化,再接着完成自身数据部分的初始化。 YSC5-3-3 b)若该构造函数没有初始化数据列表 无条件调用基类中的无参构造函数完成继承数据部分的初始化 YSC5-3-4 第⑶步骤: 执行派生类对象构造函数的函数体语句。 派生类的析构函数: 基类的析构函数函数不能被继承。 派生类对象的析构: 先析构自身数据部分,再析构继承数据部分。 派生类的拷贝构造函数: 如果派生类未定义拷贝构造函数,则C++编译器将自动产生缺省的拷贝构造函数,且调用缺省的拷贝构造函数时系统将调用基类的拷贝构造函数。 如果派生类定义拷贝构造函数,则需要为基类相应的拷贝构造函数传递参数。 单继承的继承方式: 准备知识: 关于protected成员的用法: 在单类的情况下,protected成员的用法与private成员的用法一致。 YSC5-4 ⑴公有基类(classB: publicA) 基类的public(除了构造函数和析构函数)成员和protected成员等价于派生类的public成员。 注意: ①基类对象不能访问基类自身的private和protected成员。 ②基类的public和protected成员对于派生类可见。 但是,基类的protected成员只能被派生类的public成员使用,而不能直接被派生类的对象使用;基类的public成员能直接被派生类的对象使用。 通常,基类可以将不允许基类自身对象使用,但是,却可以让派生类可以使用的方法设计成protected成员,如界面程序。 YSC5-5 类型兼容规则: 在需要基类对象的任何地方,都可以使用公有派生类对象来替代。 ⑴派生类对象可以给基类对象赋值。 ⑵派生类对象可以初始化基类的引用。 ⑶派生类对象的地址可以赋值给指向基类的指针。 ※在替代之后,派生类对象就可以作为基类的对象使用,但是,只能使用从基类继承的成员。 YSC5-6、YSC5-7、YSC5-8 ⑵私有基类(classB: privateA) 基类的public成员和protected成员等价于派生类的private成员。 注意: 基类的public和protected成员对于派生类可见。 但是,基类的public和protected成员只能被派生类的成员函数使用,而不能直接被派生类的对象使用。 YSC5-9 ⑶保护基类(classB: protectedA) 基类的public成员和protected成员等价于派生类的protected成员。 如果是classB: protectedA,则此现象等同于private继承,但是,若classB: publicA,然后,classC: protectedB,则protected继承才与private继承有所区别。 YSC5-10 ※从类的继承角度看,派生类的对象可以有条件的使用基类中的方法和自身所包含的继承数据部分的属性。 ※从类的封装角度看,不允许派生类的对象不受限制的使用继承的数据部分,即便是使用,也应该使用基类中定义的方法来使用继承数据部分。 派生类成员的隐藏规则: 在类的派生层次结构中,基类的成员和派生类的新增成员都具有类的作用域,二者的范围不同。 如果派生类声明了一个和基类成员同名的新成员,派生类的新成员就隐藏了基类的同名成员,直接使用成员名只能访问到派生类的成员。 再直接说,如果派生类声明了一个和基类成员函数同名的新成员函数,即使函数的参数不同,从基类继承的同名函数的所有重载形式也会被隐藏。 YSC5-11 思考题: 输入学生的考试成绩,显示学位平均分。 要求: 学生姓名应该去除无效字符;学生考试成绩应该进行合理性验证。 多态: 针对类而言,多态是指同样的类成员函数调用针对不同的对象有不同的实现,即实际调用的是不同的函数。 面向对象的多态分为: 重载多态: 比如普通函数以及类成员函数的重载,运算符重载。 强制多态: 比如在运算时的强制类型转换。 以上两种多态也被称为专用多态 包含多态: 比如定义在不同类中的同名成员函数的多态行为,是通过虚函数来实现的。 参数多态: 与类模板相关。 以上两种多态也被称为通用多态 虚函数: 体现了类的多态特性,并且引入了动态联编的概念,是动态绑定的基础。 当被调函数中的形参为基类的引用对象或对象的指针时才考虑虚函数使用的现象。 继承是虚函数使用的前提,虚函数的使用体现了类的多态特性。 YSC6_V0 YSC6_V0程序中GS类public继承S类,但同时这两个类均需要定义对象完成相应的工作,其中的score函数在两个类中的定义很相似但有区别,因此fun1函数和fun2函数从形式上看非常相似,能否只写一个这样的函数? YSC6_V1 YSC6_V1程序中fun函数的定义若写成voidfun(S&a)时编译是正确的,main主函数中函数调用fun(a);语句的执行是正确的。 但是,函数调用fun(b);语句的执行结果是错误的(程序执行了但结果是错误的)。 YS68_V1程序中fun函数的定义若写成voidfun(GS&a)时编译出现错误,因为,继承是单向的,基类对象a做实参时,不能为派生类的形参对象所引用。 即main主函数中函数调用fun(a);语句编译出现错误。 还能否实现刚才的问题? 继承“召唤”多态——使用虚函数技术(仅仅对于对象的指针和引用的间接访问才会放生多态的现象)。 虚函数的实现: 在成员函数定义时加关键字virtual。 YSC6-1 YSC6-1程序中类A的成员函数f在定义时加关键字virtual,表示该函数为虚函数,在类A的派生类中该成员函数可以有不同的定义实现。 YSC6-1程序中的x.f()的函数调用反映了多态性,其中的形参x是基类的对象引用。 在该程序被编译时不能立即确定f()的确切位置,即不能确定f()是基类的f()还是派生类的f(),但是编译
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 类定义及其应用 定义 及其 应用