静态成员和友元_第1页
静态成员和友元_第2页
静态成员和友元_第3页
静态成员和友元_第4页
静态成员和友元_第5页
已阅读5页,还剩21页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

静态成员和友元引入考虑一个学生管理系统,有时候需要向系统添加一个学生,有时候需要从系统中删除一个学生。我们希望能够随时显示该系统中的所有学生,并且能够随时获取系统中的学生总数。使用链表来存储所有的学生。要求在一个常数时间内得到系统中的学生总数。第2页,共28页,2024年2月25日,星期天6.1静态成员-背景使用类数据成员保存链表头指针和学生总数的作法是不可取的,因为每个该类的对象都拥有一份副本,需要花费精力来维护这些副本的一致性(这些副本必须完全相同)。设置全局变量来保存链表头指针和学生总数的作法可行,但是不够安全,因为程序的任何地方都可以修改这两个变量(导致程序的行为异常)。【演示】第3页,共28页,2024年2月25日,星期天6.1静态成员-引入可以使用静态成员解决上述问题。静态成员包括两种:静态数据成员:使用关键字static修饰的数据成员。静态成员函数:使用关键字static修饰的成员函数。补充:C语言中使用static关键字描述了变量或者函数的可见性。全局静态变量/对象是只在当前源文件中可见的全局变量或者对象。示例不同源文件中可以定义同名的全局静态变量,但是各自占用的内存不同。局部静态变量/对象是只在函数局部作用域内可见的变量或者对象,其生存期等于程序的生存期。静态函数是只在当前源文件中可见的函数。不同源文件中可以定义同名的静态函数。示例第4页,共28页,2024年2月25日,星期天6.2静态数据成员使用static关键字修饰数据成员。静态数据成员不属于该类的任何对象,而是由该类的所有对象共享。静态数据成员是属于类的,而不是属于对象的。一个类的静态数据成员只有一个,无论该类目前有多少个对象。公有的静态数据成员可以在程序的任何地方访问,非公有的静态数据成员只能在类的作用域内或者友元中访问。classStudent{

…private:charm_name[20];Student*m_pNext;Student*m_pPrev;

staticStudent*m_pListHead;};第5页,共28页,2024年2月25日,星期天6.2静态数据成员-访问在类的成员函数中可以直接访问此类的静态数据成员,无需使用任何成员访问算符(.和->)。在非成员函数中可以以两种方式访问一个类的公有静态数据成员。通过该类的对象,使用.和->算符访问使用类名限定的成员名来访问Student::Student(char*name):m_pNext(NULL),m_pPrev(NULL){

…//每构造一个对象,学生总

//数应该加1++s_nStudentCount;}Students(“aa”);Studentt(“bb”);cout<<s.s_nCount;//OKcout<<t.s_nCount;//OKcout<<Student::s_nCount;//OK//以下断言成立assert(&s.s_nCount==&t.s_nCount);assert(&s.s_nCount==&Student::s_nCount);第6页,共28页,2024年2月25日,星期天6.2静态数据成员-唯一性一个对象占用空间的大小和静态数据成员大小无关,换句话说:一个对象占用空间的大小是由该类所有非静态数据成员大小之和决定的。默认的拷贝构造行为会拷贝静态成员吗?静态数据成员所占用的空间不会随着对象的产生而分配,也不会随着对象的销毁而回收。静态数据成员具有静态生存期。静态数据成员保存在程序的全局数据区中。在main函数开始运行之前就已经准备好了。在main函数返回之后结束其生存期。第7页,共28页,2024年2月25日,星期天classStudent{public:Student(char*name);~Student();private:charm_name[20];Student*m_pNext;Student*m_pPrev;

staticStudent*s_pListHead;};voidmain(){Student*p1=newStudent(“a”);Student*p2=newStudent(“b”);Student*p3=newStudent(“c”);…}Heap“aaaa”m_pNextm_pPrev“bbbb”m_pNextm_pPrev“cccc”m_pNextm_pPrev全局数据区s_pListHeadStackp3p2p1OS运行状态第8页,共28页,2024年2月25日,星期天6.2静态数据成员-初始化类定义中只是声明了静态数据成员的存在(不会为其分配空间),还需要定义该静态数据成员(为其分配内存空间)。静态数据成员的定义不能放在头文件中,否则当多个源文件都包含该头文件时,将产生重复定义的链接错误。静态数据成员的定义应该放在源文件中。定义和初始化静态数据成员的语法:<静态数据成员类型><类名>::<静态数据成员名>=<值>;<静态数据成员类型><类名>::<静态数据成员名>(<值>);第9页,共28页,2024年2月25日,星期天静态成员的初始化//Student.hclassStudent{public:Student(char*name);~Student();private:charm_name[20];Student*m_pNext;Student*m_pPrev;

staticStudent*s_pListHead;staticint s_nCount;};//Student.cpp#include“student.h”Student*Student::s_pListHead=NULL;intStudent::s_nCount=0;Student::Student(char*name){

…}Student::~Student(){

…}第10页,共28页,2024年2月25日,星期天6.2静态数据成员静态数据成员的类型可以是其所属类,而非静态数据成员只能声明为该类对象的指针或者引用。classStudent{public:

…private: staticStudents_leader;//OK Student*m_pLeader;//OK Studentm_leader;//ERROR};静态数据成员从其行为上看和全局对象类似,但是使用静态数据成员可以实现数据隐藏,静态数据成员可以是private的,全局对象则不能。静态数据成员不会和全局变量发生名字冲突。第11页,共28页,2024年2月25日,星期天6.2静态数据成员-勘误教材P335的例子前的描述是不准确的。如果说s.nextStudent未被执行,则程序运行的结果应该是0。实际上此段代码在VC6,VC.NET中的运行结果都是1;在Dev-C++环境中的运行结果是0;说明不同的编译器会对此段代码产生不同的结果。所以在实际编程中,应该总是使用::的方式来访问静态数据成员。【演示3】第12页,共28页,2024年2月25日,星期天6.3静态成员函数-引入使用static关键字修饰的成员函数。静态成员函数和静态数据成员类似,不属于任何对象,而是属于类的。只要类存在就可以使用其静态成员函数,与该类当前是否创建过对象或者创建了几个对象无关。静态成员函数的定义和非静态的成员函数定义一样,在静态成员函数定义时不能使用static。第13页,共28页,2024年2月25日,星期天//Student.hclassStudent{public:Student(char*name);~Student();char*GetName();

staticvoidPrintAllStudents();private:charm_name[20];Student*m_pNext;Student*m_pPrev;

staticStudent*m_pListHead;};//Student.cpp#include“student.h”Student*Student::m_pListHead=NULL;Student::Student(char*name){…}Student::~Student(){…}voidStudent::PrintAllStudents(){…}第14页,共28页,2024年2月25日,星期天6.3静态成员函数-对象无关静态成员函数不与任何对象相联系,因而静态成员函数没有this指针,即调用静态成员函数时不会隐含的传递this指针。因为静态成员函数没有this指针,所以静态成员函数不能声明为const。因为静态成员函数没有this指针,因此可以将其赋给一个函数指针。typedefvoid(*PROC)();PROCproc=Student::PrintAllStudent;第15页,共28页,2024年2月25日,星期天6.3静态成员函数因为静态成员函数没有this指针,所以在静态成员函数中不能直接访问类的非静态成员,而必须通过该类的一个对象使用成员访问算法访问类的非静态成员。在静态成员函数中可以直接访问类的其他静态数据成员或者静态成员函数(无论其是否公有)。在非静态成员函数中可以直接调用该类的静态成员函数。第16页,共28页,2024年2月25日,星期天//Student.cpp#include“student.h”…voidStudent::PrintAllStudents(){//直接访问静态成员,OKStudent*q=s_pListHead;while(q!=NULL){//通过对象访问公有非静态成员,OKcout<<q->GetName()<<endl;//通过对象访问私有非静态成员,OKcout<<q->m_name<<endl;//通过对象访问私有非静态成员,OKq=q->m_pNext;}}//Student.hclassStudent{public:Student(char*name);~Student();char*GetName(){returnm_name;}

staticvoidPrintAllStudents();private:charm_name[20];Student*m_pNext;Student*m_pPrev;

staticStudent*m_pListHead;};第17页,共28页,2024年2月25日,星期天6.3静态成员函数-调用在类的作用域外调用类的静态成员函数有两种途径:通过对象使用成员访问算符访问。

Students;s.PrintAllStudent();通过类名限定的函数名访问,建议使用该方式,因为这种方式的调用更加明确的说明了此函数的归属,也不会出现教材P335页可能存在的问题。

Student::PrintAllStudents();静态成员函数从其行为上看更象是普通函数。不过静态成员函数定义于类的作用域中,因而能够任意访问类的非公有成员。普通函数则不能访问类的非公有成员。第18页,共28页,2024年2月25日,星期天6.4示例-对演示1的改进使用静态数据成员和静态成员函数修改演示1的代码。链表的头指针和链表的长度设为静态数据成员。将打印链表元素的函数设为Student类的静态成员函数,从而使得PrintAllStudents从原来类功能的使用者变为了类功能的提供者。【演示】第19页,共28页,2024年2月25日,星期天6.4示例如何使一个类的对象只能在堆上建立?也就是说不能定义该类的全局对象,也不能定义该类的局部对象,而只能从堆中为对象分配内存。将构造函数声明为私有可以防止类的使用者构造此类的对象,但是同时也禁止了从堆上构造对象。静态成员函数能够任意访问此类的成员函数,包括构造函数。【演示】第20页,共28页,2024年2月25日,星期天6.4示例如何使一个类只能创建一个对象?将构造函数声明为私有可以防止类的使用者构造此类的对象。静态数据成员的类型可以是其所属类。演示单件(Singleton)是编程中的惯用手法。如一个系统中的权限控制类第21页,共28页,2024年2月25日,星期天//a.cppstaticintg_nCount;//此变量只能在a.cpp中使用voidAddToList(Node*pHead,Node*pToInsert){

…++g_nCount;}voidRemoveFromList(Node*pHead,Node*pToDelete){

…--g_nCount;}//b.cpp//errorC2159:morethanonestorageclassspecifiedexternstaticintg_nCount;externintg_nCount;voidPrintListLength(){

//errorLNK2001:unresolvedexternalsymbol"intg_nCount"

cout<<g_nCount<<endl;}第22页,共28页,2024年2月25日,星期天//a.cppintg_nCount;static

voidAddToList(Node*pHead,Node*pToInsert)//只在a.cpp中使用{

…++g_nCount;}voidRemoveFromList(Node*pHead,Node*pToDelete){

…--g_nCount;}//b.cppstaticvoidAddToList(Node*pHead,Node*pToInsert);voidAddToList(Node*pHead,Node*pToInsert);voidRemoveFromList(Node*pHead,Node*pToDelete);voidmain(){

…AddToList(p,q);//errorC2129:staticfunctiondeclaredbutnotdefined}AddToList(p,q);//errorLNK2001:unresolvedexternalsymbol‘AddToList’第23页,共28页,2024年2月25日,星期天6.5友元在类的作用域外不能访问一个类的非公有成员。类可以声明一个普通函数、其他类的成员函数或者另一个类为其友元,从而向友元暴露所有非公有成员。由于友元不是类的成员,故类中声明友元的位置不受成员访问限定符的影响,可以在public区,也可以在private区,效果完全一样。友元声明主要用于运算符重载(后面的章节解释)友元破坏了封装性和数据隐藏,实际中应尽量少用或者不用。第24页,共28页,2024年2月25日,星期天6.5友元-普通函数一个类可以在其类定义中声明某个普通函数为其友元,从而使该函数可以任意访问其非公有成员。在类中使用friend关键字声明友元函数,格式为:friend<类型><友元函数名>(<参数表>);教材P344页下面的代码存在一处语法错误,错在哪里?使用友元实现矩阵和矢量的乘法。classMatrix;classVector{

friendVectorMultiply(Matrix&,Vector&);};classMatrix{…friendVectorMultiply(Matrix&,Vector&);};第

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论