资源预览内容
第1页 / 共31页
第2页 / 共31页
第3页 / 共31页
第4页 / 共31页
第5页 / 共31页
第6页 / 共31页
第7页 / 共31页
第8页 / 共31页
第9页 / 共31页
第10页 / 共31页
亲,该文档总共31页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第15章 静态成员与友元类是类型而不是对象,每个类的对象都是该类数据成员的拷贝。然而,往往需要让类的所有对象在类的范围内共享某个数据。声明为static的类成员便能在类范围中共享,称之为静态成员。友元函数完全是普通的C+函数,不同的是,它可以访问类的保护成员或私有成员,方便编程,提高了效率,但却破坏了类的封装。15.1静态成员的需要性15.2静态成员的使用15.3静态数据成员15.4静态成员函数15.5需要友元的原因15.6友元的使用#include#includeintnextStudentID=0;ClassStudentIDpublic:StudentID()value=+nextStudentID;cout“Assigningstudentid”valueendl;StudentID()-nextStudentID;cout“Destructingid”valueendl;protected:intvalue;ClassStudentpublic:Student(char*pName=“noName”)cout“Constructingstudent”pNameendl;strcpy(name,pName);namesizeof(name)-1=0;protected:charname20;StudentIDid;Voidmain()Students(“Randy”);运行结果:Assigningstudentid1ConstructingstudentRandyDestructingid1全局变量nextStudentId,它既不能放在头文件中定义,也不能放在类StudentId的内部实现中,只能放在应用程序中主函数main()的前面。在重用StudentId类的时候,总是还要额外的考虑一个全局变量的处置,这不得不使类的封装性受到伤害。返回一个类的属性是该类所有对象的属性,而不是这个类某个对象的属性。因此,我们需要一种能标是类的属性而不是单个对象属性的方法。这就是静态成员的作用。一个类的静态数据成员是用来表示类的属性的成员,而不是对象的属性的成员。成员有数据成员和成员函数之分,静态成员也有静态数据成员和静态成员函数之分。静态成员用static声明。#include#includeclassStudentpublic:Student(char*pName=noname)coutcreateonestudentn;strncpy(name,pName,40);name39=0;noOfStudents+;/静态成员:每创建一个对象,学生人数增1coutnoOfStudentsendl;Student()coutdestructonestudentn;noOfStudents-;/每析构一个对象,学生人数减1coutnoOfStudentsendl;staticintnumber()/静态成员函数returnnoOfStudents;protected:staticintnoOfStudents;/若写成noOfStudents=0;则非法charname40;intStudent:noOfStudents=0;/静态数据成员在类外分配空间和初始化voidfn()Students1;Students2;coutStudent:number()endl;/调用静态成员函数用类名引导voidmain()fn();coutStudent:number()endl;/调用静态成员函数用类名引导运行结果:createonestudent1createonestudent22destructonestudent1destructonestudent00数据成员noOfStudents,既不是对象s1也不是对象s2的一部分。Student类随着对象的产生,每个对象都有一个name成员值,但无论对象有多少,甚至没有,静态成员noOfStudent也只有一个。所有Student对象都共享它,并且能够访问它。在Student对象空间中,是没有静态数据成员noOfStudents的,它的空间,不会随着对象的产生而分配,或随着对象的消失而收回。所以它的空间分配并不在Student的构造函数里完成,并且空间回收也不在类的析构函数里完成。静态数据成员确实是在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。这样,它的空间分配有三个可能的地方:(1)一是作为类的外部接口的头文件,那里有类声明;(2)二是类定义的内部实现,那里有类的成员函数定义;(3)三是应用程序的main()函数前的全局数据声明和定义处。静态数据成员要实际地分配空间,故不能在类声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸与规格”,并不进行实际的内存分配,所以类声明中写成定义“staticintnoOfStudents=0;”是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。静态数据成员也不能在main()函数之前的全局数据声明处定义,因为那样会使每个重用该类的应用程序在包含了声明该类的头文件后,都不得不在应用程序中再定义一下该类的静态成员。File1.cpp/student类的内部实现部分#include“student.h”/类的成员函数定义(没有包括静态数据成员定义)File2.cpp/应用程序重用了student类#include“student.h”#includeintStudent:noOfStudents=0 /不便于重用voidfn()Students1;Students2;cout“Student:number()endl;Voidmain()fn();coutStudent:number()endl;静态数据成员是类的一部分,静态数据成员的定义是类定义的一部分,将其放在类的内部实现部分中定义是再合适不过了。定义时,要用类名引导。重用该类时,简单的包含其头文件便可。下面程序将Ch15_1.cpp改成多文件程序实现结构:/*/*student.h*/*classStudentpublic:student(char*pName=”noname”);Student();staticintnumber();/静态成员函数protectd: staticintnoOfStudents;charname40;/*/*Student.cpp*/*#include#include#includeint Student : noOfStudents=0;/在类的内部实现中分配空间和初始化student(char*pName)count“createonestudentn”;strcpy(name,pName);noOfStudents+;/静态成员:每创建一个对象,学生人数增1coutnoOfStudentsendl;Student:Student()cout”destructonestudentn”;noOfStudents-;/每析构一个对象,学生人数减1coutnoOfStudentsendl;intStudent:number()/静态成员函数returnnoOfStudents;/*/*ch15_2.cpp*/*#include#includevoidfn()students1;students2;coutStudent:number()endl;voidmain()fn();coutStudent:number()endl;工程文件ch15_2.prj中包含:student.cppch15_2.cpp运行结果为:createonestudent1createonestudent22destructonestudent1destructonestudent00返回公共静态数据成员可被类的外部访问,保护或私有静态数据成员只可被类的内部访问。classStudentpublic:Student()noOfStudents+;/staticintnoOfStudents;/公共静态数据成员/;voidfn(Student&s1,Student&s2)couts1.noOfStudents;/此处也可以访问静态数据成员在类的外部,访问静态数据成员的形式可以是s1.noOfStudents,它等价于s2.noOfStudents,更通常的用法是Student:noOfStudents(不能用Student.noOfStudents)。其意义是,静态数据成员是属于Student类的,而不是属于那个特定对象的,它也不需要依赖某个特定对象的数据。静态数据成员用的比较多的场合一般为:(1)用来保存流动变化的对象个数(如noOfStudents);(2)作为一个标志,只是一个特定的动作是否发生(如可能创建几个对象,每个对象要对某个磁盘进行写操作,但显然在同一时间里只允许一个对象写文件,在这种情况下,用户希望说明一个静态数据成员指出文件何时正在使用,何时处于空闲状态);(3)一个指向一个链表第一成员或最后一个成员的指针。返回静态成员函数定义是类的内部实现,属于类定义的一部分。它的定义位置与一般成员函数一样。与静态数据成员一样,静态成员函数与类相联系,不与类的对象相联系,所以访问静态成员函数时,不需要对象。如果用对象去引用静态成员函数,只是用其类型。#includeclassStudentpublic:staticintnumber()returnnoOfStudents;/protectd:Charname40;StaticintnoOfStudents;int Student : noOfStudents=1;voidmain()Students;couts.number()t;/ok用对象引导静态成员函数coutStudent:number()endl;/ok用类名引导静态成员函数运行结果为:11一个静态成员函数不与任何对象相联系,故只能访问类的静态数据成员,不能访问类的非静态数据成员。非静态成员函数可以访问类的静态数据成员,也可以访问非静态数据成员。因为类的静态成员总是存在的。#includeclassStudentpublic:staticchar*sName()/静态成员函数是所有对象共享的coutnoOfStudentsendl;returnname;/error哪个对象?protectd:Charname40;StaticintnoOfStudents;int Student : noOfStudents=0;voidfn()/Students;couts.sName()endl;/sName()从对象s上得到的是Student类型返回我们有家人,也有朋友,朋友又可以分为知心朋友和一般朋友。我们可以和家人一起共享家庭的秘密,这些秘密对外界是不公开的。但是,有一些特殊的朋友,和我们相处就像一家人一样,我们家的秘密对他们是公开的。与此类似,一个类的非公有成员一般是不能被其他函数使用的,但是,如果有一些函数与这个类关系密切,那么,可以通过将这个函数定义为这个类的“友元函数”来为这个函数提供直接访问类的非公有成员的能力。同样,如果一个类A与另一个类B的关系非常密切,那么可以通过将类A定义为类B的“友元类”来为类A的所有成员函数提供直接访问类B的非公有成员的能力。有时候,普通函数需要直接访问一个类的保护或私有数据成员。如果没有友元机制,则只能将类的数据成员声明为公共的,从而,任何函数都可以无约束的访问它。普通函数需要直接访问类的保护或私有数据成员的原因主要是为提高效率。返回申明一个函数f()是另一个类B的友元函数的方式是:在类B的定义体中说明函数f(),并且在类名前面加上friend关键字:ClassBfriend函数f()的原型声明;/其他类成员的定义或声明;在类B中声明的友元函数f:(1)它不是A的函数成员。(2)f的定义可以在类B的说明内,也可以在类外。(3)函数f虽不是A的成员,但有权访问和调用A的所有私有及保护成员。申明一个类A是另一个类B的友元类的方式是:在类B的定义体中说明类A,并且在类名前面加上friend关键字:ClassBfriendclassA;/其他类成员的定义或声明;在类B中说明的友元类A:(1)它可能是与B无关的另外一个类。(2)类在类外说明(3)类A有权访问和调用类B的所有成员,包括私有及保护成员。当将一个类A或函数f()说明为另一个类B的友元后,类A或函数f()就可以直接访问类B的任何成员,包括私有和保护成员,这一点就像是一个家庭的知心朋友可以知晓这个家庭的所有秘密一样。使用友元的注意事项:(1)friend不是双向的,即如果将类A定义为类B的友元,那么类A就可以直接访问类B的私有成员,但这并不意味着类B的成员函数可以直接访问类A的私有成员,需要在类A中说明类B为它的友元类。(2)友元不是类的成员。也就是说,友元不受类成员访问控制的限制,它只是一个说明,可以放在类定义体中的任何地方。public,private等限定符对其不起作用。(3)除了一般的函数可以是友元函数外,一个类的成员函数也可以成为另一个类的友元函数,这时的友元说明需要加上类域的限定。intfunc(int,float);/一个一般函数classApublic:voidmemfunc(char*);/类A的成员函数/;classBfriendintfunc(int,float);friendvoidA:memfunc(char*);/类B的其他成员;面向对象程序设计主张程序的封装,数据的隐藏,不过任何事物都不是绝对的,友元的概念是C+语言为用户提供的在局部打破这种封装和隐藏的手段,好像一个家庭的财物,总是要通过防盗门、门锁、保险柜等措施不让外人接触。但在特殊情况下,例如全家出游,又需要检查煤气、水、电等情况,就不得不把钥匙交给可依赖的朋友。这位朋友就是友元。不过友元的概念有点类似于结构程序设计语言中的goto语句,虽有必要设置,但不宜多用。返回
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号