c++基础教案PPT课件_第1页
c++基础教案PPT课件_第2页
c++基础教案PPT课件_第3页
c++基础教案PPT课件_第4页
c++基础教案PPT课件_第5页
已阅读5页,还剩134页未读 继续免费阅读

下载本文档

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

文档简介

.,1,目录,第一节c+概述第二节函数第三节类和对象第四节友元和重载第五节模板第六节继承,.,2,第一节c+概述,main()函数标准输入输出流exit语句数据类型标准库string类型引用类型指针和const限定符动态内存分配,.,3,1.1main()函数,intmain()return0;每个c+程序必须含有main()函数,且main函数是唯一被操作系统显式调用的函数定义函数必须制定4个元素:返回类型、函数名、形参表、函数体。操作系统通过main的返回值来确定程序是否成功执行完毕,返回0表示程序成功执行完毕,通常非0表示有错误出现,.,4,1.2标准输入输出流,C+没有直接定义进行输入/输出的任何语句,这个功能由标准库iostream.h提供。包含两个类:输入流istream和输出流ostream.#includeusingnamespacestd;标准库中的四个IO对象cin标准输入(如键盘),为istream对象cout标准输出(如显示屏),为ostream对象cerr标准错误,用于输出警告和错误信息,为ostream对象clog用于产生程序执行的一般信息,为ostream对象,.,5,cin读入流(由键盘输入),作用从键盘取得数据送至内存,与一起使用结合方向为自左向右例如:intv1,v2;cinv1v2;从流中读取信息时,输入流缓冲指针跟踪最后一个读入到流的字符,每次尝试从流获取信息时,都从指针的当前位置开始cin自动跳过空白字符(whitespace)返回值为左操作数,.,6,用cout写入到流(输出到屏幕),cout必须与输出操作符一起使用,结合方向为自左向右例如:coutEntertwonumbersendl;coutdecx;(hex/oct)作用将右操作数插入到cout中,可同时接受不同类型的数据输出,所以可有多个s;/从第一个非空字符读至下一个空白字符读入一行getline(cin,line);两个参数:输入流对象和string对象功能:从输入流的中读取内容到line中,换行符是该行的结束标志注:getline()不忽略开头的换行符,但line并不保存换行符,即若开头遇换行符,line为空string输出:coutsstr;输入了一次回车,这个语句遇到回车就结束,这样,回车符也跟着到了内存,到用getline()再输入时,内存里第一个字符就是回车,所以getline()一读到这个回车就结束了,所以getline(cin,str);不起作用。解决:处理掉cinstr;遗留的回车符,可以在该语句下加一句:getchar();/吸收内存里的回车符,.,12,string对象的操作s.empty();若字符串为空,返回trues.size();返回s中字符的个数sn;返回s中位置为n的字符s1=s2;比较,所有的比较运算符都可以使用s1+=s2;连接s1=s2;赋值,string类型可以和字符串字面值连接,赋给string类型,但是,+操作符的左右操作数必须至少有一个string类型strings=hello+,+s1;,.,13,1.6引用类型,引用就是对象的另一个名字,主要用于函数的形参引入voidswap(inta,intb)inttemp=a;a=b;b=temp;intmain()intx=10,y=20;swap(x,y);形参与实参有各自不同的内存空间,若实参是一个复杂对象,重新分配内存会引起程序执行效率大大下降形参对实参为值传递,对形参的任何改变不会引起实参值的改变,.,14,1.6.1非const引用,引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。声明:类型标识符/定义引用ra,它是变量a的引用(别名)(1)1.引用必须在定义时初始化,int,.,16,1.6.2const引用,格式:const类型标识符/正确引用型参数应该在能被定义为const的情况下,尽量定义为const。,.,17,对const对象的引用,constintival=1024;inti=34;constintrefval是对const型的引用,所以任何对refval的赋值都是非法const对象必须用const引用const引用可以绑定到相关的类型的对象,或绑定到右值,.,18,1.6.3指针和引用的比较,指针和引用都可间接访问另一个值,但是1、引用总是指向某个对象,定义时必须初始化。指针则可以在任何时候被初始化2、引用一旦被初始化,就不能改变引用的关系而指针则可以随时改变所指向的对象3、赋值:引用即为别名,给引用赋值修改的是该引用所关联的对象的值(非const引用)而指针可以更改其指向的对象,也可以更改所指向的对象的值4、不能有NULL引用,引用必须与合法的存储单元关联,而指针则可以是NULL,.,19,1.7指针和const限定符,使用const修饰指针时,由于const的位置不同,而含意不同。1.7.1指向const对象的指针指向const的指针,不可以通过该指针修改对象,但可以其他方式修改doubledval=3.14,pi=3.1415;constdouble*ptr=/合法,.,20,若一个指针是指向const对象,则该指针必须具有const特性。例如constdoublepi=3.14159;constdouble*ptr=/错误,const对象要用指向const的指针来指向,这样可以保证既不能通过*ptr,也不能通过pi修改其值指向const的指针常用作函数的形参,这样可以确保函数的实参在函数调用过程中不被修改voiduse_ptr(constint*p),.,21,1.7.2const指针,固定指向一个对象的指针,即指针本身是常量char*constptr1=stringptr1;/const放在类型说明和变量之间ptr1=stringptr2;/非法,指针本身的值不可改变*ptr1=m;/合法指针所指的变量的值可以改变若指针及指针所指向的变量的值都不可以更改constchar*constptr=stringptr;,.,22,1.8动态内存分配,一内存分配有三种方式从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。堆,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。,.,23,1.8.2单个对象的动态分配与释放,动态分配:由关键字new及其后面的类型指示符构成。该类型指示符可以是内置类型或class类型。例:newint;从堆中分配了一个int型的对象。newStudent;分配了一个Student类对象。需要注意的是堆中分配的对象没有名字。new表达式返回了一个指向该对象的指针,对该对象的全部操作都要通过这个指针间接完成。例如:int*pi=newint;该new表达式创建了一个int型的对象,由pi指向它。初始化int*pi=newint(0);该语句表示pi指向一个int型的对象,该对象的初始值为0。括号中的表达式被称作初始化式,.,24,动态内存的释放,与静态分配内存的对象不同,编译器不会自动释放它们所占的内存-除非整个程序结束。所以当动态分配内存的对象完成它的使命,需要被销毁的时候不能依赖编译器,而要靠程序员用delete释放。格式:deletepi;/释放pi所指向的内存空间指针pi本身是个在全局域中声明的全局对象,它的生命期由编译器控制。pi的存储区在程序开始之前就被分配,一直保持到程序结束。而pi指向的对象的生命期是由程序员控制的,它是在程序执行过程中遇到new表达式时才被创建,遇到delete表达式时被销毁并收回存储区,.,25,动态内存的释放,delete只能用在指向内存是用new动态分配的指针上,如果将其在指向堆以外内存的指针上,会使程序运行期间出现未定义的行为。唯一的例外是,当指针指向NULL时,不管指针指向的对象是如何分配内存的,都不会引发麻烦。voidf()inti;char*str=asdd;int*pi=/安全!pd指向一个动态分配的对象,.,26,常见错误,1忘记了释放内存,造成内存泄露。含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存泄漏(memoryleak)。函数体内的局部变量在函数结束时自动消亡。例如p是局部的指针变量,它消亡的时候会让它所指的动态内存一起消亡。这是错觉!voidFunc(void)char*p=(char*)malloc(100);/动态内存会自动释放吗?指针消亡了,并不表示它所指的内存会被自动释放2对同一内存区应用了多次delete表达式。这通常发生在多个指针指向同一个动态分配对象的时候。若多个指针指向同一对象,当通过某一个指针释放该对象时就会发生这种情况。3内存被释放了,并不表示指针会消亡或者成了NULL指针,形成野指针,.,27,野指针,因此,当程序执行deletepi;语句时,pi指向对象的内存被释放,但指针pi本身的内存并没有受到delete表达式的影响。在deletepi;之后,pi被称作空悬指针(俗称野指针),即指向无效内存的指针。空悬指针是错误的根源,它很难被检测到,如果对它进行操作将会产生无法预测的结果。一个比较好的办法是在指针所指的对象被释放后,马上将该指针设置为NULL,这样可以清楚地表明该指针不再指向任何对象char*p=(char*)malloc(100);strcpy(p,“hello”);free(p);/p所指的内存被释放,但是p所指的地址仍然不变if(p!=NULL)/没有起到防错作用strcpy(p,“world”);/出错,.,28,野指针,指针操作超越了变量的作用范围。这种情况让人防不胜防,示例程序如下:classApublic:voidFunc(void)coutFunc();/p是“野指针”,.,29,1.8.3数组的动态分配与释放,new表达式也可以在堆中分配数组。在这种情况下new表达式中的类型指示符后面必须有一对方括号,里面的值代表数组的长度,而且该数值可以是一个复杂的表达式。new表达式返回指向数组第一个元素的指针。动态分配数组只需指定类型和长度格式:newtypesize;/size为数组元素的长度,可以是任意表达式。new返回指向新分配数组的第一个元素的指针。例如:int*p2;p2=newintn;delete释放new分配的存储空间deletep;/表明p指向的是动态存储区的数组,如果遗漏,编译器无法发现错误,.,30,int*pi=newint(1024);/分配单个int型的对象,用1024初始化int*pia=newint1024;/分配一个含有1024个元素的int型数组,未被初始化,.,31,第二节函数,参数传递函数返回值内联函数函数的重载,.,32,2.1参数传递,C+语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。在调用函数时,对于每一个实参,其类型必须与对应的形参类型相同,或可被转换为该形参类型2.1.1普通的非引用形参为传值调用,调用时将实参的值赋给形参,而形参的任何改变不会改变实参voidswap(intp1,intp2)inttemp=p1;p1=p2;p2=temp;调用时:inta=5,b=9;swap(a,b);/a、b并没有互换,.,33,指针形参,指针传递传递的是地址,函数可以通过指针赋值,修改指针所指向的对象的值voidswap(int*p1,int*p2)inttemp=*p1;*p1=*p2;*p2=temp;调用时怎么写?1、若要保护指针指向的对象的值,则形参需定义为指向const对象的指针voiduse_ptr(constint*p)在函数体中,不能通过*p修改p指向的对象的值实参既可以为int*,也可以为constint*类型,.,34,2、如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,调用结束释放所以该参数本来就无需保护,没有必要加const修饰。例如不要将函数voidFunc1(intx)写成voidFunc1(constintx)。同理不要将函数voidFunc2(Aa)写成voidFunc2(constAa)。其中A为用户自定义的数据类型。对于非内部数据类型的参数而言,象voidFunc(Aa)这样声明的函数注定效率比较低。因为函数体内将产生A类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为voidFunc(Acinab;/输入a,b两变量的值swap(a,b);/直接以变量a和b作为实参调用swap函数coutab;/输出结果(1)被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;在传值调用中若传递的是对象,将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。,.,37,三种传递方式的比较,值传递voidFunc1(intx)x=x+10;/形参的使用intn=0;Func1(n);/调用cout“n=”nsuit=suit;this-faceUp=true;,.,63,const成员函数,使用const关键字进行说明的成员函数为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字说明的成员函数不能用来操作常对象。说明格式如下:类型说明符函数名(参数表)const;其中,const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带const关键字。任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。,.,64,classStackpublic:voidPush(intelem);intPop(void);intGetCount(void)const;/const成员函数private:intm_num;intm_data100;intStack:GetCount(void)const+m_num;/编译错误,企图修改数据成员m_numPop();/编译错误,企图调用非const函数returnm_num;,.,65,3.5构造函数与析构函数,概述构造函数的初始化表缺省的构造函数缺省的拷贝构造函数运算符重载缺省的赋值函数析构函数,.,66,3.5.1概述,每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不想编写上述函数,C+编译器将自动为A产生四个缺省的函数,如A();/缺省的无参数构造函数A(constA/缺省的赋值函数,.,67,作用:把对象的初始化工作放在构造函数中,把清除工作放在析构函数中。当对象被创建时,构造函数被自动执行。当对象消亡时,析构函数被自动执行。这样可以自动执行对象的初始化和清除工作。命名:构造函数、析构函数与类同名,由于析构函数的目的与构造函数的相反,就加前缀以示区别。构造、析构函数没有返回值,即使是void也不可以。构造、析构函数置于类定义的public部分析构函数不得带有任何参数构造函数可以有一个或多个,一旦定义一个有参的构造函数,一定要显式定义一个无参构造函数,.,68,3.5.2构造函数的初始化表,和其他函数不同,构造函数有个特殊的初始化方式叫“初始化表达式表”(初始化表)。初始化表位于函数参数表之后,以冒号开始,后面接数据成员列表,列表之间用逗号分隔,每一个变量后面用一对圆括号包含它的初始值。初始化表放在构造函数的定义中PlayingCard:PlayingCard(Suitsis,intir):suit(is),rank(ir),faceUp(true)/函数体,.,69,(2)执行顺序,首先执行初始化列表,然后执行该构造函数函数体中的语句成员对象初始化的次序完全不受它们在初始化表中次序的影响,只由成员对象在类中声明的次序决定。这是因为类的声明是唯一的,而类的构造函数可以有多个,因此会有多个不同次序的初始化表。如果成员对象按照初始化表的次序进行构造,这将导致析构函数无法得到唯一的逆序。例classxinti;intj;public:x(intval):j(val),i(j)/相当于i=j;j=val;j还没有初值就被使用;所以尽量避免使用成员来初始化其他成员x(intval):j(val),i(val),.,70,const数据成员,const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。不能在类声明中初始化const数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE的值是什么。classAconstintSIZE=100;/错误,企图在类声明中初始化const数据成员intarraySIZE;/错误,未知的SIZE;const数据成员的初始化只能在类构造函数的初始化表中进行,类的定义classAA(intsize);/构造函数constintSIZE;类的实现A:A(intsize):SIZE(size)/构造函数的初始化表生成类的对象Aa(100);/对象a的SIZE值为100Ab(200);/对象b的SIZE值为200,.,71,(3)使用说明,大多数成员可以在构造函数的初始化表中初始化,也可以在函数体中赋值。但是类的const常量和引用成员只能在初始化表里被初始化,因为不能对他们赋值,所以要在执行构造函数的函数体之前完成初始化。classConstRefpublic:ConstRef(intii);private:inti;constintci;int,.,72,ConstRef:ConstRef(intii)i=ii;ci=ii;/不能对常量赋值ri=i;/此时是对ri所绑定的对象赋值,但是还不知道ri是谁的别名ConstRef:ConstRef(intii):i(ii),ci(ii),ri(i),.,73,3.5.3缺省的构造函数,格式:A:A()当类没有定义构造函数时,编译器才会自动生成一个不带参数的缺省构造函数在缺省构造函数中,对于类中的成员,若为类类型,调用默认构造函数来初始化;若为内置或复合类型(指针、数组),若对象定义在局部作用域中,必须对它赋初值使用缺省构造函数创建对象Complexc;注:Complexc();是错误的。编译器把他解释为一个函数的声明。c是函数名,返回类型为Complex一旦定义一个有参的构造函数,一定要显式的定义一个无参构造函数,.,74,Complex:Complex(doubler,doublei)real=r;image=i;Complex:Complex(doubler)real=r;image=0;Complex:Complex()real=0;image=0;cout“Initializing00”endl;调用方式:创建对象时自动调用缺省的构造函数Complexc1;Complex*pc1=newComplex;/注意,调用缺省构造函数时,没有括号调用有参构造函数Complexc2(4.5);或Complexc2=newComplex(4.5);/一个参数Complexc3(4.5,6.2);或Complexc3=newComplex(4.5,6.2);,.,75,3.5.4缺省的拷贝(复制)构造函数,格式:A(constA,.,76,在类中,初始化对象有两种方式直接初始化:用括弧直接调用与实参匹配的构造函数。如:stringdots(10,.);stringempty;复制初始化:用等号首先使用指定构造函数创建一个临时对象,然后使用复制构造函数将那个临时对象复制到正在创建的对象。stringempty_copy=string();复制构造函数的应用场合定义一个对象并用一个同类型对象初始化时类的对象作为实参传递给形参时从函数返回时复制一个对象即使是定义了其他的构造函数,这个缺省的复制构造函数也是存在的,.,77,3.5.5运算符重载,对于基本数据类型,有许多相应的基本运算,如算术运算的+、-、*、/,但是对用户自定义类型,却不可以用这些运算符进行运算,为了使类类型也能使用这些运算符,必须对这些运算符进行重载重载操作符是具有特殊名称的函数,在C+语言中,可以用关键字operator加上运算符来表示函数名,叫做运算符重载。遵循的规则:*不能改变原运算符操作数的个数+*不能改变原运算符的自然含义*重载操作符必须具有至少一个类类型或枚举类型的操作数,既不能对内置类型对象的操作符重载。不能重载的运算符:成员运算符.作用域运算符:条件运算符?:一元运算符*,.,78,如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个参数,因为对象自己成了左侧参数。从语法上讲,运算符既可以定义为友元函数,也可以定义为成员函数。作为类的成员函数重载的格式:一元运算符返回值类型类名:operator运算符()/operator运算符作为一个整体作为函数名二元运算符返回值类型类名:operator运算符(参数说明)/参数说明只允许有一个参数,即右操作数,.,79,例如:对复数进行相加,通过+号能直接实现类内声明:Complexoperator+(constComplex,.,80,3.5.6缺省的赋值函数,格式:A,.,81,拷贝构造函数与赋值函数的比较,拷贝构造函数和赋值函数非常容易混淆,常导致错写、错用。拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。Stringa(“hello”);Stringb(“world”);Stringc=a;/调用了拷贝构造函数,最好写成c(a);c=b;/调用了赋值函数,.,82,赋值和复制,内存分配方法影响赋值的含义:1.复制语义:c+中使用,实际是调用的赋值函数,右侧变量值赋给左侧变量,两个变量占用不同存储空间,值是独立的2.指针语义:同一(Java中使用)左侧变量的地址赋给了右侧变量的地址,两个变量是共用同一地址,其中一个变量值的改变会改变另一个变量,.,83,3.5.8析构函数,在对象脱离其作用域时,系统自动执行析构函数,作善后清理工作,主要用于资源回收析构函数与类同名,无参数,无返回值。只是在函数名前加例:Complex:Complex()编译器总会自动生成一个析构函数,他按成员在类中的声明次序逆序撤销成员。默认析构函数释放对象生命期内或构造函数中获取的资源。注:动态分配的对象只有在指向该对象的指针被删除时才会撤销,若没有删除,不会运行该对象的析构函数,对象一直存在,会导致内存泄漏当对象的引用或指针超出作用域时,不会运行析构函数。当删除指向动态分配对象的指针或实际对象超出作用域时,才会运行析构函数,.,84,第四节友元和重载,友元友元函数友元类友元关系与继承重载与友元重载重载输出运算符重载输入运算符转换与类类型,.,85,4.1友元,我们已知道类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。,.,86,4.1.1友元函数,友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。1、友元函数友元函数的特点是他是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样。下面举一例子说明友元函数的应用。,.,87,#includeusingnamespacestd;classPointpublic:Point(doublexx,doubleyy)x=xx;y=yy;voidGetxy();frienddoubleDistance(Point,.,88,voidmain()Pointp1(3.0,4.0),p2(6.0,8.0);p1.Getxy();p2.Getxy();doubled=Distance(p1,p2);/友元函数被调用时与普通函数一样coutDistanceisd只能重载为成员函数二元操作符+=-=/=*=type可以是内置类型名、类名等,但是,一般不允许转换为数组或函数类型,转换为指针和引用类型是可以的转换操作符是一种特殊的类成员函数。它的功能是将类类型的值转换为其他类型的值。转换操作符在类定义体内声明,在operator后跟转换的目标类型不能指定返回类型,形参表必须为空,.,98,classSmallIntfriendostream转换函数一般不应该改变被转换的对象,所以转换操作符通常应被定义为const成员,.,99,该转换是用户为类类型对象自定义的转换,但是调用时是由系统隐式调用SmallIntsi1,si2;intmain()输入si1、si2的值;cout127)?”greaterthan”:(si1127)?”lessthan”:”equalto”);(b)?(a):(b)但是在复杂调用下,它的行为是不可预期的。例如constintsize=10;intiasize;intmain()intelem_cnt=0;int*p=每次扩展p=p+1都应用了两次。与函数不同,函数调用之前确保实参只被计算一次,.,103,函数模板,函数模板提供一种用来自动生成各种类型函数实例的算法,程序员对于函数接口参数化,而函数体保持不变。函数模板的声明在关键字template后跟随的尖括弧内可以有一个到多个参数。参数不能为空,模板参数可以是模板类型参数,他代表一种类型;也可以模板非类型参数,他代表一个常量表达式。可以在某个头文件中定义模板。例如:/filemax.h#ifndefMAX_INCLUDED#defineMAX_INCLUDEDtemplateclassTTmax(Tt1,Tt2)return(t1t2)?t1:t2;/templateclassT为模板前缀,定义和声明都由此开始#endif函数模板不是一个实在的函数,编译系统无法产生执行代码。classT定义T作为模板类型参数。max是函数名,t1和t2是其参数,返回值的类型为T,.,104,模板非类型参数是由一个普通的参数声明构成,模板非类型参数的值代表了模板定义中的一个常量例如,size代表arr指向的数组的长度templateTypemin(Type(/模板类型参数使用模板定义的余下部分中size可以出现在要求常量的地方模板类型参数作为一个类型指示符,它的使用方式与内置或用户自定义的类型完全一样,.,105,模板实例化,当实例化max()时(调用),由具体的数据类型替换。你可以像使用普通的函数那样使用max()。编译器按照所使用的数据类型自动产生相应的模板实例:intn=10,m=16;floatf1=60.4,f2=23.2;inthighest=max(n,m);/编译器知道n,m为int型,用模板生成一个具体的函数定义,并将T换成intfloatfh=max(f1,f2):/发现该调用时,又生成一个定义,将T换成float,成为模板函数complexdoublec1,c2;/.给c1,c2赋值complexdoublehigher=max(c1,c2);/complex版本类模板,.,106,显式模板实参,templateTmin(T,T)unsignedintui;intmain()min(ui,1024);min()的实参必须类型相同,此时实参推演失败,模板实例化出现错误,可以采用显式模板实参。显式模板实参被指定在逗号分隔的列表中,用尖括号括起来,紧跟在模板实例名的后面上例调用时可写成min(ui,1024);,.,107,模板编译模式,有两种:包含模式和分离模式包含模式/model1.h中templateclassTTmin(Tt1,Tt2)return(t1t2)?t1:t2;在每个使用min()实例的文件中都包含该文件/model1.cpp中#include“model1.h”inti=7,j=5;doubled=min(i,j);缺点:模板体对于用户来说应该是透明的在多个文件之间编译相同的函数模板定义增加了不必要的编译时间,.,108,分离模式:函数模板的声明放在头文件中,而定义放在cpp文件中/model2.h中templateclassTTmin(Tt1,Tt2);/model2.cpp中exporttemplateclassT/在生成其他文件使用的模板实例化时可能要用到这个定义Tmin(Tt1,Tt2)return(t1t2)?t1:t2;/user.cpp中#include“model2.h”inti=7,j=5;doubled=min(i,j);使用该模式可以很好的将定义和接口分开,但并非所有的编译器都支持该模式,.,109,类模板,(1)定义和声明templateclassArraypublic:Array(intn);T类模板的定义和声明都是以关键字template开头,关键字后面是一个逗号分隔的模板参数表,参数之间用括起来。当有多个类型参数的时候,每个参数必须以关键字class开头template,.,110,2)类模板中成员函数的实现templateArray:Array(intn)一般形式:template返回类型类模板名:成员函数名(形参)函数体注:每一个成员函数前都要加模板前缀template,每一个成员函数名前都要加类模板名:3)创建类模板的实例格式:类名对象Arrayb(10);Arrayc(8.5);,.,111,4)类模板和编译模式只有当编译器看到了实际的类模板的定义,而不仅仅是声明时,它才能实例化类模板,所以当程序使用一个类模板并要求其实例时,程序首先必须提供类模板的定义而类模板的成员函数与模板本身很相像,编译器只有看到成员函数并真正使用时,才实例化这个函数。所以一个成员函数被定义在类模板定义之外,那么这些定义应该被放在含有该类模板定义的头文件中。(包含编译模式)分离编译模式,.,112,/array.h中exporttemplate/声明Array是一个可导出的类模板classArray;array.cpp中#include“array.h”templateArray:Array(intn)/user.cpp中#include“array.h”Arraya(5);,.,113,第六节继承,关于继承的直观描述c+继承格式改写、重定义与遮蔽接口和抽象类继承和构造函数虚拟析构函数,.,114,6.1关于继承的直观描述,继承可以使一个子类的实例存取和它的父类相关的数据和行为。也就是说用继承的方法可以自动为一个类提供来自另一个类的属性和操作,进而使程序设计人员在一个一般的类的基础上很快建立一个新的类,而不必从零开始设计每个类。当一个类被其他的类继承,被继承的类称为基类,又称为父类。继承其他类的类称为派生类,又称为子类。继承的含义子类具有父类的属性和方法(扩展)子类可以声明新的属性和方法,也可以剔出不适用其用途的父类操作(收缩)继承总是向下传递的,因此一个类可以从它上面的多个超类中继承各种属性。,.,115,继承的含义,例:如果Dog是Mammal的派生类,而Mammal又是Animal的派生类,则Dog不仅继承了Mammal的属性,同时也继承了Animal的属性。派生类可以覆盖从基类继承来的行为。“是一个”检验:检验两个类是否为继承关系时,存在一项规则,这项规则称为“是一个”(is-a)检验。例如,A是B的子类,那么Ais-aB,.,116,6.2c+继承的格式,从一个基类派生的继承称为单继承。单继承声明语句的常用格式为:class派生类名:访问控制关键字基类名数据成员和成员函数声明;从多个基类派生的继承称为多继承或多重继承,也就是说,一个派生类有多个直接基类。class派生类名:访问控制关键字基类名1,访问控制关键字基类名2,.数据成员和成员函数声明;,.,117,.,118,例,classParentprivate:intthree;protected:inttwo;public:intone;Parent()one=two=three=42;voidinParent()coutonetwothree;/alllegal;classChild:publicParentpublic:voidinChild()coutone;/legalcouttwo;/legalcoutthree;/error-notlegal;voidmain()Childc;coutc.one;/legalcoutc.two;/error-notlegalcoutc.three;/error-notlegal,.,119,6.3改写、遮蔽与重定义,静态类和动态类:变量的静态类是指用于声明变量的类。静态类在编译时就确定下来,并且再也不会改变变量的动态类指与变量所表示的当前数值相关的类。动态类在程序的执行过程中,当对变量赋新值时可以改变对于静态类型面向对象编程语言,在编译时消息传递表达式的合法性不是基于接收器的当前动态数值,而是基于接收器的静态类来决定的,.,120,例,classAnimalpublic:virtualvoidspeak()cout“AnimalSpeak!”;classDog:publicAnimalpublic:voidspeak()bark();voidbark()cout“Woof!”;classBird:publicAnimalpublic:voidspeak()cout“Tweet!”;,.,121,Animal*pet;Dogfido;fido.speak();/woof!fido.bark();pet=/error!编译错误,pet为指向Animal的指针,.,122,6.3.1多态,多态:如果方法所执行的消息绑定是由最近赋值给变量的数值的类型来决定的,那么就称这个变量是多态的。Java变量都是多态的。C+声明为简单类型的变量,非多态。在c+中,编译器不会自动将派生类类型对象转换为基类类型对象Animala;Dogb;b.speak();/woof!a=b;a.speak();/Animalspeak!/当接收器的类型为Animal时,speak所执行的方法处于静态类Animal中,即使变量所包含的数值来自于不同类型的赋值,.,123,实现多态要满足的条件,a.使用指针或引用;b.父类相关方法声明为virtual;Birdb;Animal*pet,/error,.,124,Animal没有声明为virtual,classAnimalpublic:voidreply()coutreply();/AnimalReply!d.reply();/Woofagain!,.,125,6.3.2改写和虚拟方法,改写:语法上:子类定义一个与父类有着相同名称且类型签名相同的方法。运行时:变量声明为一个类,它所包含的值来自于子类,与给定消息相对应的方法同时出现于父类和子类。改写与替换结合时,想要执行的一般都是子类的方法。改写机制Java语言,只要子类通过同一类型签名改写父类的方法,自然便会发生所期望的行为。C+中需要父类中使用关键字Virtual来表明这一含义。,.,126,虚函数的定义与派生类中的重定义class类名public:virtual成员函数说明;class类名:基类名public:virtual成员函数说明;/virtualoptional,.,127,例,classApublic:virtualvoidf()coutA:fendl;classB:publicApublic:virtualvoidf()coutB:fendl;,.,128,虚函数是成员函数,如果某类的一个成员函数被说明为虚函数,就意味这该成员函数在派生类中可能有不同的实现。在c+中通过指向基类对象的指针来访问这些虚函数。classC:publicApublic:virtualvoidf()coutf();pa=,.,129,6.3.2重定义,当子类定义了一个与父类具有相同名称但类型签名不同的方法时,发生重定

温馨提示

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

评论

0/150

提交评论