c++基础教案

上传人:ning****hua 文档编号:243750163 上传时间:2024-09-30 格式:PPT 页数:139 大小:471KB
返回 下载 相关 举报
c++基础教案_第1页
第1页 / 共139页
c++基础教案_第2页
第2页 / 共139页
c++基础教案_第3页
第3页 / 共139页
点击查看更多>>
资源描述
,单击此处编辑母版标题样式,*,*,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,目录,第一节,c+,概述,第二节 函数,第三节 类和对象,第四节 友元和重载,第五节 模板,第六节 继承,1,第一节,c+,概述,main(),函数,标准输入输出流,exit,语句,数据类型,标准库,string,类型,引用类型,指针和,const,限定符,动态内存分配,2,1.1 main(),函数,int,main(),return 0;,每个,c+,程序必须含有,main(),函数,且,main,函数是唯一被操作系统显式调用的函数,定义函数必须制定,4,个元素:返回类型、函数名、形参表、函数体。,操作系统通过,main,的返回值来确定程序是否成功执行完毕,返回,0,表示程序成功执行完毕,通常非,0,表示有错误出现,3,1.2,标准输入输出流,C+,没有直接定义进行输入,/,输出的任何语句,这个功能由标准库,iostream.h,提供。包含两个类:输入流,istream,和输出流,ostream,.,#include ,using namespace std;,标准库中的四个,IO,对象,cin,标准输入(如键盘),为,istream,对象,cout,标准输出(如显示屏,),为,ostream,对象,cerr,标准错误,用于输出警告和错误信息,为,ostream,对象,clog,用于产生程序执行的一般信息,为,ostream,对象,4,cin,读入流,(由键盘输入),作用,从键盘取得数据送至内存,与,一起使用结合方向为自左向右,例如:,int,v1,v2;,cin,v1v2;,从流中读取信息时,输入流缓冲指针跟踪最后一个读入到流的字符,每次尝试从流获取信息时,都从指针的当前位置开始,cin,自动跳过空白字符(,whitespace,),返回值为左操作数,5,用,cout,写入到流(,输出到屏幕,),cout,必须与输出操作符,一起使用,结合方向为自左向右,例如:,cout,Enter two numbers,endl,;,cout,dec,x;(hex/oct,),作用,将右操作数插入到,cout,中,可同时接受不同类型的数据输出,所以可有多个,操作符,把信息写入流时,把信息添加到流的末尾,相当于从左到右输出,endl(end,of line),为操纵符,具有换行效果,并刷新与设备相关联的缓冲区,刷新后用户可立即看到写入到流中的输出。,注:忘记刷新可能会造成输出停留在缓冲区,建议在程序员调试过程中,这些语句都应刷新输出流,iomanip.h,中,,setw(n,):,为后面的输出项预留,n,列,6,1.3 exit(),语句,形式:,exit(interger_value,);,执行,exit,语句时,程序立即终止。一般来说,如果因为一个错误而调用,exit,,,就使用,1,,其他情况使用,0,。,该函数在头文件,cstdlib,中,所有要有预编译命令,#include ,using namespace std;,7,1.4,内置数据类型,常量,宏常量:,#define PI 3.1415926,系统不为其分配内存,只是简单的字符串替换,const,常量:,const,类型 常量标识符,=,值;,常量定义后不能修改,所以必须初始化,例如:,const double PI=3.1415926;,系统为,PI,分配内存单元,两种常量的比较,const,常量有数据类型,而宏常量没有数据类型;编译器可以对,const,常量进行类型合法性检查,而对宏常量仅仅是字符的替换,没有合法性检查,并且在字符替换的时候可能产生意想不到的错误,c+,中,,const,常量完全可以取代宏常量,8,布尔类型,bool,它的值只有两个,true,和,false,可以将算术类型的任何值赋给,bool,对象。,0,代表,false,非,0,代表,true,初始化:创建对象并给它赋初值(赋值指擦除对象的当前值并用新值代替),有两种形式:,复制初始化,:,用等号,int,val,=1024;,直接初始化,:,初始化式放在括号中,int,val(1024);,通常在一个对象首次使用的地方定义该对象,9,1.5,标准库,string,类型,#include ,using namespace std;,string,对象的定义和初始化,string s2(s1); /s1,为,string,对象或字符串字面值,string s3(n,c);/s3,为,n,个,c,注意:字符串字面值和标准库,string,类型不是同一类型,string,对象的读写,输入:,cin,s;,/,从第一个非空字符读至下一个空白字符,读入一行,getline(cin,line,);,两个参数:输入流对象和,string,对象,功能:,从输入流的中读取内容到,line,中,换行符是该行的结束标志,注:,getline,(),不忽略开头的换行符,但,line,并不保存换行符,即若开头遇换行符,,line,为空,string,输出:,cout,s,endl,;,10,#include ,#include ,using namespace std;,int,main(int,argc, char*,argv,) ,string,str,;,cout, Input a string ,str,; /,输入,str,的时候,只要遇到了空格,就会结束输入,cout,str,endl,;,cout, Input a line using,getline,endl,;,getline(cin,str,);,cout,str,str,;,输入了一次回车,这个语句遇到回车就结束,这样,回车符也跟着到了内存,到用,getline,()再输入时,内存里第一个字符就是回车,所以,getline,()一读到这个回车就结束了,所以,getline(cin,str,);,不起作用。,解决: 处理掉,cin,str,;,遗留的回车符,可以在该语句下加一句,:,getchar,();/,吸收内存里的回车符,11,string,对象的操作,s.empty();,若字符串为空,返回,true,s.size();,返回,s,中字符的个数,sn,;,返回,s,中位置为,n,的字符,s1=s2;,比较,所有的比较运算符都可以使用,s1+=s2;,连接,s1=s2;,赋值,string,类型可以和字符串字面值连接,赋给,string,类型,但是,,+,操作符的左右操作数必须至少有一个,string,类型,string s=hello+,+s1;,12,1.6,引用类型,引用就是对象的另一个名字,主要用于函数的形参,引入,void,swap(int,a ,int,b),int,temp=a; a=b; b=temp;,int,main(),int,x=10, y=20;,swap(x,y);,形参与实参有各自不同的内存空间,若实参是一个复杂对象,重新分配内存会引起程序执行效率大大下降,形参对实参为值传递,对形参的任何改变不会引起实参值的改变,13,1.6.1,非,const,引用,引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。,声明:,类型标识符,&,引用名,=,目标变量名;,int,a;,int,&,ra,=a; /,定义引用,ra,它是变量,a,的引用,(,别,名),(,1,),&,在此不是求地址运算,而是起标识作用。,(,2,)声明引用时,必须同时对其进行初始化。,(,3,)引用声明完毕后,相当于目标变量名有两个名称,不能再把该引用名作为其他变量名的别名。,(,4,)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元。故:对引用求地址,就是对目标变量求地址。,&,ra,与,&a,相等。,14,非,const,引用,int,ival,=1024;,1.,引用必须在定义时初始化,,int,&,refval,=,ival,;,一旦绑定到某对象不可以将引用,int, ,绑定到另一对象,int,&refval2=,refval,;,2.,不能定义引用类型的引用,,int, ,必须用与该变量同类型对象初始化,int,3.,引用对应的值必须具有相应的内存空间,以便对这个空间进行引用。常量、表达式、引用不能赋给引用,例:,int,i = 5;,int,j = 6;,int,/ k,和,i,的值都变成了,6;,15,1.6.2 const,引用,格式:,const,类型标识符,&,引用名,=,目标变量名;,用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为,const,,,达到了引用的安全性。,int,a ;const,int,&,ra,=a;,ra,=1; /,错误,a=1; /,正确,引用型参数应该在能被定义为,const,的情况下,尽量定义为,const,。,16,对,const,对象的引用,const,int,ival,=1024;,int,i=34;,const,int,&,refval,=,ival,;,refval,=512;,int,&ref2=,ival,;,const,int, const,int,const,int,refval,是对,const,型的引用,所以任何对,refval,的赋值都是非法,const,对象必须用,const,引用,const,引用可以绑定到相关的类型的对象,或绑定到右值,17,1.6.3,指针和引用的比较,指针和引用都可间接访问另一个值,但是,1,、引用总是指向某个对象,定义时必须初始化。,指针则可以在任何时候被初始化,2,、引用一旦被初始化,就不能改变引用的关系,而指针则可以随时改变所指向的对象,3,、赋值:引用即为别名,给引用赋值修改的是该引用所关联的对象的值,(,非,const,引用,),而指针可以更改其指向的对象,也可以更改所指向的对象的值,4,、不能有,NULL,引用,引用必须与合法的存储单元关联,而指针则可以是,NULL,18,1.7,指针和,const,限定符,使用,const,修饰,指针,时,由于,const,的位置不同,而含意不同。,1.7.1,指向,const,对象的指针,指向,const,的指针,不可以通过该指针修改对象,但可以其他方式修改,double,dval,=3.14,pi=3.1415;,const,double,*,ptr,= &,dval,;,/const,限定了指针所指的对象类型,非,ptr,*,ptr,=,3.1415,; /,不合法,指针所指的对象不能改,ptr,= &,pi,; /,合法,,,指针值可变,dval,=3.1415; / /,合法,19,若一个指针是指向,const,对象,则该指针必须具有,const,特性。例如,const double pi=3.14159;,const double *,ptr,=/,正确,double *ptr1=,/,错误,,const,对象要用指向,const,的指针来指向,这样可以保证既不能通过*,ptr,,,也不能通过,pi,修改其值,指向,const,的指针常用作函数的形参,这样可以确保函数的实参在函数调用过程中不被修改,void,use_ptr(const,int,*p),20,1.7.2 const,指针,固定指向一个对象的指针,即指针本身是常量,char * const pt,r,1 = stringpt,r,1;,/,const,放在类型说明和变量之间,ptr1 = stringpt,r,2;,/,非法,指针本身的值不可改变,*,ptr1 = m;,/,合法,指针所指的变量的值可以改变,若指针及指针所指向的变量的值都不可以更改,const char * const,ptr,=,stringptr,;,21,1.8,动态内存分配,一 内存分配有三种方式,从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,,static,变量。,栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。,堆,亦称动态内存分配。程序在运行的时候用,malloc,或,new,申请任意多少的内存,程序员自己负责在何时用,free,或,delete,释放内存。动态内存的生存期由我们决定如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。,22,1.8.2,单个对象的动态分配与释放,动态分配:由关键字,new,及其后面的类型指示符构成。该类型指示符可以是内置类型或,class,类型。,例:,new,int,;,从堆中分配了一个,int,型的对象。,new Student ;,分配了一个,Student,类对象。,需要注意的是堆中分配的对象没有名字。,new,表达式返回了一个指向该对象的,指针,,对该对象的全部操作都要通过这个指针间接完成。,例如:,int,*pi = new,int,;,该,new,表达式创建了一个,int,型的对象,由,pi,指向它。,初始化,int,*pi = new,int,(0) ;,该语句表示,pi,指向一个,int,型的对象,该对象的初始值为,0,。括号中的表达式被称作初始化式,23,动态内存的释放,与静态分配内存的对象不同,编译器不会自动释放它们所占的内存,-,除非整个程序结束。所以当动态分配内存的对象完成它的使命,需要被销毁的时候不能依赖编译器,而要靠程序员用,delete,释放。,格式:,delete pi;/,释放,pi,所指向的内存空间,指针,pi,本身是个在全局域中声明的全局对象,它的生命期由编译器控制。,pi,的存储区在程序开始之前就被分配,一直保持到程序结束。,而,pi,指向的对象的生命期是由程序员控制的,它是在程序执行过程中遇到,new,表达式时才被创建,遇到,delete,表达式时被销毁并收回存储区,24,动态内存的释放,delete,只能用在指向内存是用,new,动态分配的指针上,如果将其在指向堆以外内存的指针上,会使程序运行期间出现未定义的行为。唯一的例外是,当指针指向,NULL,时,不管指针指向的对象是如何分配内存的,都不会引发麻烦。,void f(),int,i;,char *,str,= ,asdd,;,int,*pi = ,short *,ps,= NULL;,double *pd = new double(123.3);,delete,str,;/,危险!,str,指向的不是动态对象,delete pi;/,危险!,pi,指向的对象,i,是一个局部对象,delete,ps,;/,安全!,ps,指向,NULL,delete pd;/,安全!,pd,指向一个动态分配的对象,25,常见错误,1,忘记了释放内存,造成内存泄露。含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存泄漏,(memory leak),。,函数体内的局部变量在函数结束时自动消亡。例如,p,是局部的指针变量,它消亡的时候会让它所指的动态内存一起消亡。这是错觉!,void,Func(void,)char *p = (char *) malloc(100);/,动态内存会自动释放吗?,指针消亡了,并不表示它所指的内存会被自动释放,2, 对同一内存区应用了多次,delete,表达式。这通常发生在多个指针指向同一个动态分配对象的时候。若多个指针指向同一对象,当通过某一个指针释放该对象时就会发生这种情况。,3,内存被释放了,并不表示指针会消亡或者成了,NULL,指针,形成野指针,26,野指针,因此,当程序执行,delete pi;,语句时,,pi,指向对象的内存被释放,但指针,pi,本身的内存并没有受到,delete,表达式的影响。在,delete pi;,之后,,pi,被称作空悬指针(俗称野指针),即指向无效内存的指针。空悬指针是错误的根源,它很难被检测到,如果对它进行操作将会产生无法预测的结果。一个比较好的办法是在指针所指的对象被释放后,马上将该指针设置为,NULL,,这样可以清楚地表明该指针不再指向任何对象,char *p = (char *) malloc(100);,strcpy(p, “hello”);,free(p,);,/ p,所指的内存被释放,但是,p,所指的地址仍然不变,if(p,!= NULL)/,没有起到防错作用,strcpy(p, “world”);/,出错,27,野指针,指针操作超越了变量的作用范围。这种情况让人防不胜防,示例程序如下:,class A public:void,Func(void,),cout, “,Func,of class A” ,Func,();/ p,是“野指针”,28,1.8.3,数组的动态分配与释放,new,表达式也可以在堆中分配数组。在这种情况下,new,表达式中的类型指示符后面必须有一对方括号,里面的值代表数组的长度,而且该数值可以是一个复杂的表达式。,new,表达式返回指向数组第一个元素的指针。,动态分配数组只需指定类型和长度,格式:,new,typesize,;,/size,为数组元素的长度,可以是任意表达式。,new,返回指向新分配数组的第一个元素的指针。例如:,int,*p2; p2=new,intn,;,delete,释放,new,分配的存储空间,delete p;,/,表明,p,指向的是动态存储区的数组,如果遗漏,编译器无法发现错误,29,int,*pi = new,int,(1024) ;,/,分配单个,int,型的对象,用,1024,初始化,int,*,pia,= new,int,1024 ;,/,分配一个含有,1024,个元素的,int,型数组,未被初始化,30,第二节 函数,参数传递,函数返回值,内联函数,函数的重载,31,2.1,参数传递,C+,语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。在调用函数时,对于每一个实参,其类型必须与对应的形参类型相同,或可被转换为该形参类型,2.1.1,普通的非引用形参,为传值调用,调用时将实参的值赋给形参,而形参的任何改变不会改变实参,void,swap(int,p1,int,p2),int,temp=p1; p1=p2; p2=temp; ,调用时:,int,a=5,b=9;,swap(a,b); / a,、,b,并没有互换,32,指针形参,指针传递传递的是地址,函数可以通过指针赋值,修改指针所指向的对象的值,void,swap(int,*p1,int,*p2),int,temp,=*p1; *p1=*p2; *p2=,temp,; ,调用时怎么写?,1,、若要保护指针指向的对象的值,则形参需定义为指向,const,对象的指针,void,use_ptr(const,int,*p),在函数体中,不能通过*,p,修改,p,指向的对象的值,实参既可以为,int,*,也可以为,const,int,*,类型,33,2,、,如果输入参数采用“值传递”,由于,函数,将自动产生临时变量用于复制该参数,调用结束释放所以该参数本来就无需保护,没有必要加,const,修饰。,例如不要将函数,void Func1(int x),写成,void Func1(const,int,x),。同理不要将函数,void Func2(A a),写成,void Func2(const A a),。其中,A,为用户自定义的数据类型。,对于非内部数据类型的参数而言,象,void,Func(A,a),这样声明的函数注定效率比较低。因为函数体内将产生,A,类型的临时对象用于复制参数,a,,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为,void,Func(A,&a),,因为“引用传递”仅借用一下参数的别名而已,34,2.1.2,引用形参,引用的一个重要作用就是作为,函数,的参数。,c,语言中当有大块数据作为参数传递的时候,采用的方案往往是,指针,,在,C+,中又增加了一种同样有效率的选择(在某些特殊情况下又是必须的选择),就是引用,void,swap(int,&p1,int,&p2)/,形参,p1, p2,都是引用,int,p; p=p1; p1=p2; p2=p; ,当调用该函数,在相应的主调函数的调用点处,直接以,变量名,作为实参进行调用即可,而不需要实参变量有任何的特殊要求。如:对应上面定义的,swap,函数,相应的主调,函数,可写为:,35,main( ),int,a,b;,cin,ab; /,输入,a,b,两变量的值,swap(a,b); /,直接以变量,a,和,b,作为实参调用,swap,函数,cout,a b; /,输出结果,(,1,)被调函数的形参就成为原来主调函数中的实参变量或对象的一个,别名,来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调,函数,中)的操作。,(,2,)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;在传值调用中若传递的是对象,将调用拷贝构造,函数,。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。,36,三种传递方式的比较,值传递,void Func1(int x)x = x + 10; /,形参的使用,int,n = 0;Func1(n); /,调用,cout, “n = ” n ,endl,;/ n = 0,指针传递,void Func2(int *x)(* x) = (* x) + 10;/,形参的使用,int,n = 0; /,调用,Func2(,cout, “n = ” n ,endl,;/ n = 10,37,引用传递:,void Func3(int ,int,n = 0;Func3(n);,cout, “n = ” n ,endl,;/ n = 10,对比上述三个示例程序,会发现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?答案是“用适当的工具做恰如其分的工作”。,38,const,引用,若不希望在函数体中通过引用来修改实参,可以使用,const,引用,int,search(const T& x),/x,为实参的别名,不用重新开辟空间,且不允许对,x,进行修改,bool,isShorter(const,string & s1 , const string & s2), return s1.size() s2.size();,一般将不需要修改的引用形参都定义为,const,引用,而且可以增强其灵活性。(对于非,const,形参,实参不能为,const,对象,也不能为字面值常量),39,传递指向指针的引用,void,swap(int,* &,v,1,int,* &,v,2),/,v1,是一个引用,,与指向,int,型对象相关联,int,*,v,;,v,=,v,1;,v,1=,v,2;,v,2=,v,; ,调用:,int,*pa=,int,*,pb,=,swap(pa,pb,);,40,2.2,函数返回值,在系统调用处将函数的返回值放在一个临时空间,在调用的地方使用该返回值(赋给一个变量或进行运算),调用语句执行完,释放临时空间,1,),返回引用:没有复制返回值,相反返回的是对象本身,格式:,类型标识符,&,函数,名(形参列表),函数,体,非,const,引用,Chain &,Chain:delete(int,k),const,引用 :不能更改返回值,const string &,shorterString(const,string & s1, const string & s1), return s1.size() s2.size()?s1:s2; ,一般来说,函数返回的引用是对函数的某个参数的引用,而这个参数本身也是引用类型。,41,引用作为返回值,必须遵守以下规则:,不能返回局部变量的引用。主要原因是局部变量会在,函数,返回后被销毁,因此被返回的引用就成为了,无所指,的引用,程序会进入未知状态。,const string &,manip(const,string & s),string ret=s;,return ret;,42,2.3,内联函数,在程序执行函数调用时,要求在转去前要保护现场保存地址,转回后先要恢复现场,并按原来保存地址继续执行。因此,函数调用要有一定的时间和空间开销,影响其效率。特别是对于一些函数体代码不是很大,但又频繁地被调用的函数来讲,解决其效率问题更为重要。因此引入内联,函数,。,内联,函数,的定义方法,定义内联函数的方法很简单,只要在函数,定义,的头前加上关键字,inline,即可。,43,inline,isNumber,(char,ch,);,/,函数,原型声明,inline,即内联函数,void,main(),char,c,h,;,cin.get(ch,);,if (,isNumber,(ch,),cout,是数字字符,endl,;,else,cout,不是数字字符,=0&,ch,=0,&,ch,=,9,)?1:0 .,该函数在编译时被替代,这样就避免了频繁调用,函数,对栈内存重复开辟所带来的消耗。但是会增加目标程序代码量,进而增加空间开销,可见它是以空间换取时间。,44,说明:,(,1,)定义内联函数只是一种请求,而不是命令编译器一定这么做,(,2,)内联函数应该在头文件中定义,(3) inline,只适合函数体内代码简单的函数使用,不能包含复杂的结构控制语句例如,while,、,switch,,,并且内联函数本身不能直接调用递归函数,(,自己内部还调用自己的,函数,),。,45,2.4,函数重载,重载可分为函数重载和运算符重载,函数重载,在,C+,程序中,可以将语义、功能相似的几个函数用同一个名字表示,即函数重载。这样便于记忆,提高了,函数,的易用性。例如函数,EatBeef,EatFish,EatChicken,可以用同一个,函数,名,Eat,表示,用不同类型的参数加以区别。,void,EatBeef,(,); /,可以改为,void Eat(Beef,);,void,EatFish,(,); /,可以改为,void Eat(Fish,);,void,EatChicken,(,);,/,改为,void Eat(Chicken,);,46,例如:,int,sum(int,a,int,b),double sum(double a, double b),调用重载函数时,编译器会根据实参的类型、个数、顺序去调用相应的函数。,int,sum1=sum(5,6);,double sum2=sum(3.4, 5.6);,注:仅仅返回类型不同是不行的,47,第三节 类和对象,类的定义和实现,类的静态成员,类的对象的定义和使用,类的成员函数,构造函数与析构函数,48,第三节 类和对象,类是一种复杂的数据类型,它是将不同类型的,数据,和与这些数据相关的,操作,封装在一起的集合体。这有点像,C,语言,中的结构,唯一不同的就是结构没有定义所说的“数据相关的操作”,即“方法”,封装,外部用户只能看到定义抽象行为的操作集合,程序员通过数据变量维护对象内部的状态,49,3.1,类的定义和实现,类的定义一般地分为说明部分和实现部分。,说明部分,是用来说明该类中的成员,包含数据成员的说明和成员函数的说明。“干什么”,实现部分,是用来对成员,函数,的定义。 “怎么干”。,类的一般定义格式如下:,class,类名,/ class,是定义类的关键字,,是种标识符,通常用字母,C,开头,public:,公有成员,函数,或数据成员的说明,private:,私有数据成员或成员,函数,的说明,;,/,注意:分号不可缺,50,类的定义,访问权限修饰符或访问控制修饰符:公有的,(public),、,私有的,(private),和保护的,(protected),public,:,公有部分往往是一些操作,(,成员函数,),,它是提供给用户的,接口,功能。这部分成员不仅可以在类内被访问,也可以在类外被访问。,private,:,私有部分通常是一些数据成员,描述该类中的对象的,属性,,用户是无法访问它们的,只有成员函数或经特殊说明的,函数,才可以引用它们,它们是被用来隐藏的部分。,protected:,可以被本类及其继承类使用,51,一个日期类定义的例子,/,类的说明部分,一般放在,date.h,文件中,class,Cdate, public:,void,setDate(int,y,int,m,int,d);,int,IsLeapYear,();,void Print();,private:,int,year, month, day; ;,/,类的实现部分 一般放在,date.cpp,文件中,#include “,date.h,”,void,Cdate:setDate(int,y,int,m,int,d),这里出现的作用域运算符,:,year = y; month = m; day = d; ,是用来标识某个成员,函数,是属于哪,int,Cdate:IsLeapYear,(),个类,如果成员函数定义在类体外,要使用作用域运算符,:,return(year%4=0 ,void,Cdate:Print,(); ,cout,year,montb,day,endl,; ,52,类的定义说明,定义类时应注意的事项,1,)在类体中不允许对所定义的数据成员进行初始化。,2,)一般地,在类体内先说明公有成员,它们是用户所关心的,后说明私有成员,它们是用户不感兴趣的。在说明数据成员时,一般按数据成员的类型大小,由小至大说明,这样可提高时空利用率。,3,)习惯地将类定义的说明部分放在头文件中,.h,。,而实现部分放在,.,cpp,文件中,53,3.1.2,类的实现,类成员函数的实现部分一般放在,.,cpp,文件中,如果要在类外使用类中的实例变量或者在类外定义函数体,要使用作用域运算符,用来标识某个成员是属于哪个类的。,格式 返回值类型 类名,:,函数名(参数列表),例如:,void,Cdate:setDate(int,y,int,m,int,d) ,year = y;,month = m;,day = d; ,54,作用域运算符,class,PlayingCard,public:,enum,Suits Spade, Diamond, Club, Heart;,Suits suit () return,suitValue,; ,int,rank () return,rankValue,; ,private:,Suits,suitValue,;,int,rankValue,;,boolean,faceUp,;,;,如:在类定义之外使用表示花色的常量,if(aCard.suit,()=,PlayingCard:Diamond,) ,55,3.2,类的静态成员,被一个类的所有实例共享的公共数据成员。静态成员,(static),可以实现同一个类的多个对象之间的数据共享。,静态数据成员对于类的所有对象只开辟了一块内存空间(在静态存储区),其中一个对象对其改变,保证所有对象存取的是更新后的相同的值。,静态数据成员的使用方法和注意事项如下:,1,、,定义,:静态数据成员在定义或说明时前面加关键字,static,。,2,、,初始化,:格式如下:,:=,:,初始化在类体外进行,前面不加,static,和访问权限控制符,private,,,public,等。,初始化时使用作用域运算符来标明它所属类,因为静态数据成员是类的成员,而不是对象的成员。,静态数据成员是静态存储的,必须对它进行初始化。,3,、,引用,:,56,class,Myclass,public:,Myclass(int,a,int,b,int,c);void,GetNumber,();void,GetSum,();private:,int,A, B, C;static,int,Sum;,实现,int,Myclass:Sum,= 0;,Myclass:Myclass(int,a,int,b,int,c)A = a;B = b;C = c;Sum += A+B+C;,调用,Myclass,M(3, 7, 10),N(14, 9, 11);,57,3.3,类的对象的定义和使用,对象的定义,有两种方法,class,类名, ,对象名,;,2.,也可以先声明类,然后定义类的对象,类名 对象名;,编译系统为对象分配内存并且将这段内存空间与对象名称进行绑定,并进行必要的初始化操作,.,变量声明只是创建了一个标识变量的名称,大部分面向对象语言通过,new,操作符来创建对象,,c+,语言可以将他们结合起来,如,PlayingCard,aCard,;,/,在,java,中必须写成,PlayingCard aCard=new PlayingCard(),;,58,使用对象,:在调用成员函数时,必须指明对类的哪个对象进行操作。 利用成员运算符,.,表明从属关系,用于类的对象,例如:,Cdate,date,*p;,date.setDate(2007, 7, 20);,p=&date,;,p-,IsLeapYear,(); /,等价于,date.IsLeapYear,();,59,3.4,类的成员函数,成员函数的定义和声明,:类的所有成员必须在类定义的,内声明。类的成员函数既可以在类内定义,以可以在类外定义,他可以访问该类的,private,成员,如:,bool,same_isbn(const,SalesItem,&r)const,return,isbn,=,r.isbn,;,没有前缀的,isbn,指的是用于调用函数的对象的,isbn,调用,调用函数时,实际是使用对象来调用的,if(total.same_isbn(trans,) /r,是,trans,的引用,60,this,指针,每个成员函数都有一个额外的、隐含的形参将该成员函数与调用该函数的类对象绑定在一起,这个形参为,this,。,调用成员函数时,形参,this,初始化为调用函数的对象的地址。所以,total.same_isbn,(trans),可以理解为,same_isbn(&total,trans,)/,第一个参数为隐含的,它传递的是调用对象的地址,使用,this,在函数体中可以显式地使用,bool,same_isbn(const,SalesItem,&r)const,return this-,isbn,=,r.isbn,;,61,当成员函数中函数参数与数据成员同名时,需要,this,指针,class,PlayingCard,PlayingCard,(,int,suit,int,rank),this-rank=,rank,;,this-suit=,suit,;,this-,faceUp,=true;,62,const,成员函数,使用,const,关键字进行说明的成员函数为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用,const,关键字说明的成员函数不能用来操作常对象。,说明格式如下:,类型说明符,函数,名,(,参数表,) const,;,其中,,const,是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带,const,关键字。,任何不会修改数据成员的函数都应该声明为,const,类型。如果在编写,const,成员函数时,不慎修改了数据成员,或者调用了其它非,const,成员,函数,,编译器将指出错误,这无疑会提高程序的健壮性。,63,class Stack,public:,void,Push(int,elem,);,int,Pop(void);,int,GetCount(void,) const; / const,成员,函数,private:,int,m_num;,int,m_data100;,;,int,Stack:GetCount(void,) const,+ m_num; /,编译错误,企图修改数据成员,m_num,Pop(); /,编译错误,企图调用非,const,函数,return m_num;,64,3.5,构造函数与析构函数,概述,构造函数的初始化表,缺省的构造函数,缺省的拷贝构造函数,运算符重载,缺省的赋值函数,析构函数,65,3.5.1,概述,每个类只有一个,析构函数,和一个,赋值函数,,但可以有多个,构造函数,(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类,A,,,如果不想编写上述,函数,,,C+,编译器将自动为,A,产生四个缺省的,函数,,如,A(); /,缺省的无参数构造,函数,A(const A /,缺省的拷贝构造,函数,A(); /,缺省的析构,函数,A,/,缺省的赋值,函数,66,作用,:把对象的初始化工作放在构造函数中,把清除工作放在析构函数中。当对象被创建时,构造函数被自动执行。当对象消亡时,析构,函数,被自动执行。这样可以自动执行对象的初始化和清除工作。,命名,:构造函数、析构函数与类同名,由于析构函数的目的与构造,函数,的相反,就加前缀,以示区别。,构造、析构函数,没有返回值,,即使是,void,也不可以。,构造、析构函数置于类定义的,public,部分,析构函数,不得带有任何参数,构造函数,可以有一个或多个,一旦定义一个有参的构造函数,一定要显式定义一个无参构造函数,67,3.5.2,构造,函数,的初始化表,和其他函数不同,构造函数有个特殊的初始化方式叫“初始化表达式表”(初始化表)。初始化表位于函数参数表之后,以冒号开始,后面接数据成员列表,列表之间用逗号分隔,每一个变量后面用一对圆括号包含它的初始值,。,初始化表放在构造函数的定义中,PlayingCard,:,PlayingCard,(Suits is,int,ir,),:,suit(is,),rank(ir),faceUp(true,) ,/,函数体,68,(,2,) 执行顺序,首先执行初始化列表,然后执行该构造函数函数体中的语句,成员对象初始化的次序完全不受它们在初始化表中次序的影响,只由成员对象在类中声明的次序决定。这是因为类的声明是唯一的,而类的构造函数可以有多个,因此会有多个不同次序的初始化表。如果成员对象按照初始化表的次序进行构造,这将导致析构函数无法得到唯一的逆序。,例,class x,int,i;,int,j;,public:,x(int,val):j(val),i(j,) /,相当于,i=j;j=,val;j,还没有初值就被使用,;,所以尽量避免使用成员来初始化其他成员,x(int,val):j(val),i(val,),69,const,数据成员,const,数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其,const,数据成员的值可以不同。不能在类声明中初始化,const,数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道,SIZE,的值是什么。,class Aconst,int,SIZE = 100;,/,错误,企图在类声明中初始化,const,数据成员,int,arraySIZE,;/,错误,未知的,SIZE;,const,数据成员的初始化只能在类构造函数的初始化表中进行,,类的定义,class A ,A(int,size);/,构造函数,const,int,SIZE ; ;,类的实现,A:A(int,size) :,SIZE(size,),/,构造函数的初始化表, ,生成类的对象,A a(100);/,对象,a,的,SIZE,值为,100 A b(200);/,对象,b,的,SIZE,值为,200,70,(,3,)使用说明,大多数成员可以在构造函数的初始化表中初始化,也可以在函数体中赋值。但是,类的,const,常量和引用成员只能在初始化表里被初始化,因为不能对他们赋值,所以要在执行构造函数的函数体之前完成初始化。,class,ConstRef,public:,ConstRef(int,ii);,private:,int,i;,const,int,ci,;,int,&,ri,;,;,71,ConstRef:ConstRef(int,ii),i=ii;,ci,=ii;,/,不能对常量赋值,ri,=i;,/,此时是对,ri,所绑定的对象赋值,但是还不知道,ri,是谁的别名,ConstRef:ConstRef(int,ii):i(ii),ci(ii),ri(i,),72,3.5.3,缺省的构造函数,格式:,A:A(),当类没有定义构造函数时,编译器才会自动生成一个不带参数的缺省构造函数,在缺省构造函数中,对于类中的成员,若为类类型,调用默认构造函数来初始化;若为内置或复合类型(指针、数组),若对象定义在局部作用域中,必须对它赋初值,使用缺省构造函数创建对象,Complex c;,注:,Complex c();,是错误的。编译器把他解释为一个函数的声明。,c,是函数名,返回类型为,Complex,一旦定义一个有参的构造函数,一定要显式的定义一个无参构造函数,73,Complex:Complex(double r , double i), real=r; image=i;,Complex:Complex(double r), real=r; image=0;,Complex:Complex() real=0; image=0;,cout,“,Initializing 0 0,”,endl,;,调用方式:,创建对象时自动调用缺省的构造函数,Complex c1;,Complex,*pc1 = new,Complex,;,/,注意,调用缺省构造函数时,没有括号,调用有参构造函数,Complex,c2,(,4.5,),;,或,Complex c2=new Complex(4.5); /,一个参数,Complex,c3,(,4.5 , 6.2,),;,或,Complex c3=new Complex(4.5,6.2);,74,3.5.4,缺省的拷贝,(,复制,),构造函数,格式,:,A(const A &a),他只有一个参数,且是所在类的对象的引用,作用,:利用编译器提供的拷贝构造函数创建新的对象,将形参对象中的成员逐个的拷贝到新的对象中。即新对象初始化为原对象的副本,Cpoint,:,Cpoint(const,Cpoint,&,o,bj,),x=,obj.x,; y=,obj.y,;,若已经定义并初始化了,Cpoint,对象,c1,则可以定义,Cpoint,c2=c1;,相当于调用,c2.Cpoint(c1);,75,在类中,初始化对象有两种方式,直接初始化:用括弧,直接调用与实参匹配的构造函数。如:,string dots(10,.); string empty;,复制初始化,:,用等号,首先使用指定构造函数创建一个临时对象,然后使用复制构造函数将那个临时对象复制到正在创建的对象。,string empty_cop
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 小学资料


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!