版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
本章知识点:1.C++面向对象程序设计的特点2.C++的输入输出3.函数的重载与有默认参数的函数4.变量的引用5.其它C++的扩充内容:const常量、new/delete运算符*C++面向对象程序设计的特点1.结构化程序设计的特点程序的主体是由函数组成(包括主函数)。主函数中输入原始数据,处理数据,输出结果。其它函数是自定义函数。主函数可以调用标准库函数,也可以调用自定义函数。采用自顶向下方式进行设计。先设计主函数,再设计其它函数。2.面向对象程序设计的特点程序主体主要由类定义组成。*C++的输入输出1.C++注释、头文件、主函数返回值等与C语言不同。2.C++的输出输出通过使用输出流对象cout来完成。格式:cout<<表达式1;其中<<是插入运算符。表达式的类型可以是基本数据类型或字符指针类型。可以连续输出,格式:cout<<表达式1<<表达式2<<……<<表达式n;(1)采用默认格式输出:cout<<1234<<""<<12.34<<""<<'A'<<""<<"abcdef"<<endl;cout标准输出流对象,<<流插入运算符,不需要给出输出数据的格式控制符。(2)改变默认的输出格式:使用输入输出流的控制符,表7-1(182页)。(a)输出八进制、十六进制整数(b)改变精度:改变有效位数(默认格式)或小数点后数字的个数(定点形式与指数形式)(c)改变字段宽度(d)改变填充字符、输出左对齐或右对齐、输出正数的加号(f)终止已设置的输出格式(例如取消左对齐)例1_1输出一个有n行的三角形****************注意:控制第1个*的字段宽度3.C++的输入输入通过使用输入流对象cin来完成。格式:cin>>变量名;其中>>为提取运算符。可以连续输入,不需要给出输入数据的格式控制符*函数的重载与有默认参数的函数1.函数的重载(1)重载的概念函数名相同,参数个数或参数类型不同。(2)重载的作用功能相近的函数,取同样的名字,减少记忆多个函数名的麻烦,使用方便。(3)调用重载函数的方法调用重载函数时,根据实际参数的个数及类型决定调用哪一个函数。注意:(1)C++的重载函数如果仅仅是返回类型不同,则是不够的。例如下面的声明是错误的。intmax(int,int);voidmax(int,int);编译器无法区分函数调用"max(3,5)”是指上述哪一个重载函数。因此重载函数至少在参数个数、参数类型有所不同。(2)typedef定义的类型是与一个已存在的类型相同,而不能建立新的类型,所以不能用typedef定义的类型名来区分重载函数声明中的参数。例如,下面的代码实际上是同一个函数。typedefINTint;voidfunc(intx){//...}voidfunc(INTx){//...)//error:函数重复定义编译器不能区分这两个函数的差别,INT只不过是int的另一种称呼而已。(3)让同名函数具有相同的功能。如果定义一个abs()函数而返回的却是一个数的平方根,该程序的可读性受到破坏。2.有默认参数的函数=1\*GB2(1)默认参数的目的C++可以给函数定义默认参数值。通常,调用函数时,要为函数的每个参数给定对应的实参。如有以下函数声明和函数定义:voiddelay(intloops);//函数声明voiddelay(intloops)//函数定义{ if(loops==0) return;for(inti=0;i<loops,i++);//为了延时}无论何时调用delay()函数,都必须给loops一个实参以确定时间。但有时需要用相同的实参反复调用delay()函数。C++可以给参数定义默认值。如果将delay()函数中的loops定义成默认值1000,只需简单地把函数声明改为:voiddelay(intloops=1000);这样,调用delay()函数时,不给出实参,程序会自动将实参当作值1000进行处理。delay(2500);//loops设置为2500delay();//ok:loops采用默认值1000调用中,若不给出参数,则按指定的默认值进行工作。允许函数默认参数值,是为了让编程简单,让编译器做更多的检查错误工作。=2\*GB2⑵默认参数的声明默认参数在函数声明中提供,当既有声明又有定义时,定义中不允许出现默认参数。如果函数只有定义,则默认参数才可出现在函数定义中。voidpoint(int=3,int=4);//声明中给出默认值voidpoint(intx,inty)//定义中不允许再给出默认值{ cout<<x<<endl;cout<<y<<endl;}=3\*GB2⑶默认参数的顺序规定如果一个函数中有多个默认参数,则形参分布中,默认参数应从右至左逐渐定义。当调用函数时,只能向左匹配参数。例如:voidfunc(inta=1,intb,intc=3,intd=4);//errorvoidfunc(inta,intb=2,intc=3,intd=4);//okvoidfunc(inta,intb,intc=3,intd=4);//okvoidfunc(inta,intb,intc,intd=4);//ok注意:函数有默认参数时一般不能再进行重载,防止调用时出现二义性。例1_2求3个数中的最大数*变量的引用1.引用的概念2.引用声明引用声明需要注意的问题:(1)引用声明时必须赋初值,声明后不能改变(2)初值类型与引用类型要一致如:intx;int&xx=x;//x的类型与xx类型要一致(3)所赋初值要为定义过的变量3.引用的使用(1)利用引用访问所对应的变量(2)引用作为函数参数例1_3编写函数实现两个变量值的交换在下面的程序中,定义一个函数rank,使得主函数中的输出从小到大。#include<iostream>usingnamespacestd;intmain(){ inta,b,c; cin>>a>>b>>c; rank(a,b,c); cout<<a<<""<<b<<""<<c<<endl;}*const常量、new/delete运算符例1_4输入圆的半径,求圆的周长和面积。(其中圆周率取3.1415926)例1_5输入三角形三个边长,求三角形面积。
本章知识点:1.面向对象程序设计的基本概念2.类的定义3.对象的定义3.类的数据成员与成员函数*面向对象程序设计的基本概念1.结构化程序设计的特点程序的主体是由函数组成(包括主函数)。主函数中输入原始数据,处理数据,输出结果。其它函数是自定义函数。主函数可以调用标准库函数,也可以调用自定义函数。采用自顶向下方式进行设计。先设计主函数,再设计其它函数。存在问题:数据与处理数据的函数分离,即程序=算法+数据结构。2.面向对象程序设计的特点程序主体主要由类定义组成。数据与处理数据的函数组合,即对象=算法+数据结构,程序=(对象+对象+……)+消息。3.由结构到类。(1)结构是非基本数据类型的一种,是用户自定义的数据类型。使用结构类型时需要:定义结构类型、定义结构变量、访问结构成员。(2)类也是非基本数据类型的一种,是用户自定义的数据类型。使用类时也需要:定义类、定义类对象(即类变量)、通过类成员函数访问类的数据成员。*类的定义class类名{类的成员;//类的成员包括数据成员和成员函数,即把数据与处理数据的操作封装在一起。};(1)数据成员的声明:与结构成员类似,需给出成员类型与成员名,另外要写出访问属性,一般为私有或保护。(2)成员函数的定义:与普通函数定义类似,写在类中,访问属性一般为公有。(3)成员的访问属性:有3种,即public、protected与private,三者的区别,默认的访问属性为私有。即:class类名{private:数据成员和成员函数;protected:数据成员和成员函数;public:数据成员和成员函数;};public的成员才能被外界访问,说明成private和protected的成员外界是不能访问的。在学习继承之前,将把protected与private一样看待。(4)如果没有显式声明成员访问限定符,系统将成员默认为private。*对象的定义1.对象定义格式类名对象名1,对象名2;//定义对象时为对象分配内存空间,字节数为数据成员所占字节数的总和。2.对象成员的访问类中私有或保护数据成员在主函数(或类外其它函数中是不能直接访问的),只能通过调用公有的成员函数实现对类对象成员的访问。调用公有成员函数的格式:对象名.成员函数名(实参1,实参2,……,实参n);3.this指针调用成员函数访问成员时,成员函数如何区分要访问的是哪个对象的成员?实际上因为是通过对象调用成员函数,所以编译系统会将该对象的地址作为参数传递给被调用的成员函数,但该参数(指针)没有显示出现,该参数是隐含参数,参数名为this。(1)类中的每个成员函数都有一个隐含参数,即指向调用该成员函数的指针this(2)在成员函数中访问数据成员时,每个成员前自动加上this->num、this->name等。例2_1声明学生类,数据成员为:学号、姓名和分数,定义成员函数要求能够设置数据和显示数据。例2_2声明时间类,包括时、分、秒3个数据成员,定义成员函数要求能够设置时间和显示时间。*类的数据成员与成员函数1.数据成员2.成员函数(1)在类中定义成员函数(2)在类外定义成员函数,在类中进行声明。(3)在类外定义的成员函数,需在函数名前加:类名::。(4)在类中定义的成员函数,如不含循环语句,C++系统自动将其作为内置(inline)函数。在类外定义的成员函数如需设为内置函数,只需在声明或定义时加上inline,并将其与类声明放在一个文件中即可。(5)成员函数可以重载,也可以有可以有默认值。例2_3声明日期类,包括年、月、日3个数据成员,定义成员函数要求能够设置日期、使日期加1天、以年.月.日的格式输出日期。3.内联函数函数调用要将程序执行权转到被调用函数中,然后再返回到调用它的函数中,进函数和出函数是需要时间的,如果频繁进出函数会大大影响工作效率。特别是对于一些函数体代码不是很大,但又频繁地被调用的函数来讲,解决其效率问题更为重要。引入内联函数实际上就是为了解决这一问题。内联函数在调用时,实际不是去调用该函数,而是在编译时用函数体内容代替函数调用将该函数的代码整段插入到当前位置从而加快程序运行速度。但每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间,内联函数是以浪费空间为代价换来了时间。inline函数的声明或定义非常简单,只要在函数声明或定义前加一个inline修饰符。
如:inlineintmax(inta,intb)
{
return(a>b)?a:b;
}注意:内联函数一般适合于不存在循环和switch等复杂的结构且只有1~5条语句的小函数上,内联函数的函数体过大时,编译器会放弃内联方式,而采用普通的方式调用函数。*对象成员的访问1.通过对象名访问对象中的成员。注意:对象数组的定义。2.通过指向对象的指针访问对象中的成员。注意堆对象、堆对象数组的定义3.通过对象的引用访问对象中的成员例2_4定义一个素数类,可以求出给定范围的素数。(结构化程序设计方法与面向对象程序设计方法的比较)例2_5定义一个类,可以求出一维数组中元素的最大值。(结构化程序设计方法与面向对象程序设计方法的比较)(1)第1种解法,数据成员为inta[100];令n等于数组元素个数。(2)第2种解法,数据成员为int*a;在成员函数中令n等于数组元素个数,且aa=newint[n];(3)数组中元素值可以从键盘输入,也可以用另一数组元素的值给a数组赋值例2_6定义一个类,可以将一维数组中的元素按由小到大的顺序排序。(结构化程序设计方法与面向对象程序设计方法的比较)例2_7将一个3×4矩阵最大元素与左上角元素交换,最小元素与右下角元素交换。*面向对象程序设计方法的总结1.面向对象程序设计中,最主要的工作是设计类(类具有封装性和信息隐蔽)2.主函数代码较少,主要是定义对象与调用成员函数3.类定义与类成员函数的分离:(1)将类定义放在头文件中;(2)将类成员函数定义放入相应的源文件中(3)在主函数中将用到的类的头文件包含进来。源代码例2-5、2-6#include<iostream>usingnamespacestd;classArray{public: voidsetdata(intnn) {n=nn; for(inti=0;i<n;i++) cin>>a[i]; } intfindmax() { intmax=a[0]; for(inti=1;i<n;i++) { if(a[i]>max)max=a[i]; } returnmax; } voidshow() { for(inti=0;i<n;i++) { cout<<a[i]<<""; } cout<<endl; cout<<"max="<<findmax()<<endl; } voidsort() { inttemp; for(intpass=1;pass<n;pass++) { for(inti=0;i<n-pass;i++) { if(a[i]>a[i+1]) {temp=a[i];a[i]=a[i+1];a[i+1]=temp; } } } }private: inta[100],n;};intmain(){ Arrayarr; arr.setdata(5); arr.sort(); arr.show(); return0;}以上代码修改如下:classArray{public: voidsetdata(intnn) {n=nn;a=newint[n]; for(inti=0;i<n;i++) cin>>a[i]; }voiddel(){delete[]a;}private: int*a,n;};intmain(){ Arrayarr; arr.setdata(5); arr.sort(); arr.show();arr.del(); return0;}也可以将另一数组元素的值赋给a数组。例2-7代码#include<iostream>usingnamespacestd;classArray{public: voidsetdata(intrr) { row=rr; for(inti=0;i<row;i++) { for(intj=0;j<4;j++) cin>>a[i][j]; } } voidprocess() { intmax=a[0][0],maxi=0,maxj=0; for(inti=0;i<row;i++) { for(intj=0;j<4;j++) { if(a[i][j]>max) { max=a[i][j];maxi=i;maxj=j; } } } a[maxi][maxj]=a[0][0];a[0][0]=max; }voidshow() { for(inti=0;i<row;i++) { for(intj=0;j<4;j++) cout<<a[i][j]<<""; cout<<endl; } }private: inta[10][10],row;};intmain(){ Arrayarr; arr.setdata(3); cess(); arr.show(); return0;}以上代码修改:classArray{public: voidsetdata(intrr,intccintaa[][4]) { row=rr; for(inti=0;i<row;i++) { for(intj=0;j<4;j++) a[i][j]=aa[i][j]; } }private: inta[10][10],row,col;};intmain(){ inta[3][4]={1,2,3,4,-5,6,7,8,9,100,11,12}; Arrayarr; arr.setdata(3,4,a); cess(); arr.show(); return0;}
本章知识点:1.构造函数的特点2.析构函数的特点3.构造函数与析构函数的执行顺序4.复制构造函数的特点5.对象指针、对象引用和对象数组6.常对象与常成员*构造函数的特点1.上一章内容的回顾:定义对象时初值不确定,需要调用公有的成员函数给对象成员赋初值,C++中提供了构造函数解决这一问题。2.构造函数是类中特殊的成员函数,构造函数特点:(1)构造函数名为类名;(2)无返回类型;(3)可以带参数,可以重载;(4)定义对象时自动调用,给对象赋初值,定义之后不能再调用。例3_1定义学生类,数据成员为:学号、姓名和分数,类中有带参数和不带参数的构造函数,给数据成员赋初值。演示构造函数的自动调用注意:定义对象时,如构造函数有参数,需要在对象名后给出实际参数。例3_2(1)编写一个类CAL,求n!。(2)编写一个类CAL,求12+22+32+……+n2。(3)求给定范围的素数。3.使用构造函数时需要注意的地方:(1)构造函数的参数可以有默认值。(参数有默认值时函数一般不再重载)(2)定义构造函数时,可以采用参数初始化表的形式赋初值。(3)未定义构造函数时,系统提供默认的无参构造函数,无参构造函数不带参数,函数体为空。若已定义构造函数,则系统不再提供无参构造函数。(4)构造函数必须是公有的。*析构函数的特点1.析构函数是类中特殊的成员函数,析构函数特点:(1)析构函数名为~类名;(2)无返回类型;(3)不带参数,不可以重载,即一个类中的析构函数是唯一的;(4)释放对象时自动调用,做清理工作。(5)析构函数必须是公有的例3-3在学生类中增加析构函数,演示析构函数的自动调用例3-4在一维数组类中增加构造函数及析构函数classArray{public: Array(intnn) {n=nn;a=newint[n]; for(inti=0;i<n;i++) cin>>a[i]; }~Array(){delete[]a;}private: int*a,n;};intmain(){ Arrayarr(5);arr.sort(); arr.show(); return0;}2.使用析构函数时需要注意的地方:未定义析构函数时,系统提供默认的析构函数(函数体为空)。若已定义析构函数系统不再提供默认的。*构造函数与析构函数的执行顺序(1)局部和静态对象,按照定义的顺序构造,析构函数执行顺序与构造函数相反。即:先构造后析构,后构造先析构。举例说明(2)定义n个元素的对象数组时,调用构造函数n次,给st[0]、st[1]、……、st[n-1]赋初值。定义对象数组时如何向构造函数传递参数?Studentst[3]={Student(1001,”aaa”,80),Student(1002,”bbb”,85),Student(1003,”ccc”,85)};(3)释放n个对象的数组时调用n次析构函数,析构st[n-1]、st[n-2]、……、st[0](4)定义对象指针时不调用构造函数(5)定义对象引用时不调用构造函数例3_4分析程序的运行结果(作业1,2)程序一#include<iostream>usingnamespacestd;classMyClass{public:MyClass(intxx=0):x(xx){cout<<"Constructingx="<<x<<endl;} ~MyClass() {cout<<"Destructing\n"; }private: intx;};intmain(){MyClass*ptr=newMyClass[3];delete[]ptr;return0;}程序二:#include<iostream>usingnamespacestd;classA{public: A(inti,intj) {a=i;b=j;cout<<"Constructing\n";} ~A() {cout<<"Destructing\n";} voidprint() {cout<<a<<""<<b<<endl;}private: inta,b;};intmain(){A*p1,*p2; p1=newA(2,3); p2=newA(6,7); p1->print(); p2->print(); deletep1;deletep2;return0;}例3_5声明一个类NUM,可以对一个任意的5位整数,求出其降序数。例如,整数是82319,则其降序数是98321。例3_6声明一个类SUM,求二维数组外围各元素的和,并输出数组中各元素及所求之和。(1)第1种解法,数据成员为:inta[10][10],row,col(2)第2种解法,数据成员为:int**a,row,col;为二维数组分配内存空间a=newint*[row]for(inti=0;i<row;i++)a[i]=newint[col];(3)二维数组中元素的值可以从键盘输入,也可以用另一个二维数组的元素值赋给a。*复制构造函数的特点1.属于构造函数的一种2.参数为对象引用3.定义对象时,当参数为另一对象时自动调用Students1,s2(s1);//或Students1,s2=s1;4.使用复制构造函数需要注意的几点:(1)未定义复制构造函数时,系统提供默认的复制构造函数(参数为对象引用)。(2)调用复制构造函数的几种情况(a)定义对象时调用intmain(){Studentst(1001,"Zhang",80),st1(st); return0;}(b)调用函数,函数参数为对象时voidfunc1(Studentst2){}(c)调用函数结束返回对象值时Studentfunc2(){Studentst3;returnst3;}主函数如下:intmain(){ Studentst(1001,"Zhang",80),st1(st); func1(st);func2(); return0;}5.浅复制与深复制默认的复制构造函数只能实现浅复制,当类中的构造函数申请了资源时,需要自定义复制构造函数实现深拷贝,同时析构函数要能够释放资源。以学生类为例进行说明。例3_7为下面的程序添加一个复制拷贝构造函数,使输出结果为5。#include<iostream>usingnamespacestd;classCat{public: Cat() {itsAge=newint; *itsAge=5; } intgetAge(){return*itsAge;} voidsetAge(inta){*itsAge=a;} ~Cat(){if(itsAge!=NULL)deleteitsAge;}private: int*itsAge;};intmain(){Catc1; Catc2(c1); c2.setAge(6); cout<<c1.getAge()<<endl;}*常对象、常成员、常指针与常引用1.常对象(1)定义(2)定义时通过构造函数赋初值(3)定义后不能修改其值,即不能调用普通成员函数,只能调用常成员函数(4)特殊情况:可变数据成员mutable,可以在常成员函数中修改其值。2.常成员函数(1)定义时函数头后加const(2)能被一般对象和常对象调用(3)不能改变数据成员的值,也不允许调用非常成员函数3.指针常量、常指针(指向常对象的指针)(1)指针常量指针常量定义:Time*constptr=&noon;特点:(a)定义时赋初值,指向对象noon,定义后不能使其指向其它对象(b)可以调用非常成员函数,修改其所指向对象的值(2)常指针(指向常对象的指针)常指针定义:constTime*ptr=&noon;特点:(a)常指针定义后,可以修改其值,使其指向其它对象,ptr=&wake;(b)可以指向常对象,也可以指向非const对象(c)通过常指针不能修改对象的数据成员的值,即只能调用常成员函数,指向常对象的指针必定是常指针(d)利用常指针做函数参数,可以对共用数据进行保护4.常引用常引用声明:constTime&nn=noon;特点:(a)可以指向常对象,也可以指向非const对象(b)通过常引用不能修改对象的数据成员的值,即只能调用常成员函数,指向常对象的引用必定是常引用(c)利用常引用做函数参数,可以对共用数据进行保护例3_8在下面给出的生日BirthDay类中,增加show函数以年/月/日的格式显示生日的值,要注意保证程序能够通过编译且正确运行。#include<iostream>usingnamespacestd;classBirthDay{public:BirthDay(intyy,intmm,intdd):y(yy),m(mm),d(dd){}private:inty,m,d;};intmain(){constBirthDaybirth(1990,12,10);birth.show();return0;}例3_9在下面给出的点Point类中,增加show函数显示点的坐标值,要注意保证程序能够通过编译且正确运行。#include<iostream>usingnamespacestd;classPoint{public:Point(intxx,intyy):x(xx),y(yy){}private:intx,y;};intmain(){Point*ptr=newPoint(20,30);constPoint*cptr=ptr;cptr->show(); deleteptr; return0;}*动态创建对象和释放对象、对象的生存期1.动态创建对象动态创建对象、赋初值及释放对象,举例说明。2.动态创建对象数组动态创建对象数组、赋初值及释放对象数组(堆对象数组不能赋初值),举例说明。3.对象的生存期(1)局部对象生存期调用函数结束,释放作为函数参数的对象时调用析构函数。(2)全局对象生存期全局对象在主函数之前构造,无特殊顺序,不析构。(3)静态局部对象生存期静态对象只被构造一次,程序运行结束时析构。例3-1源代码#include<iostream>#include<string>usingnamespacestd;classStudent{public://构造函数可以带参数,参数可以有默认值,可以采用初始化列表形式赋初值 Student(longnu=0,charna[]="noname",ints=0):num(nu),score(s) {strcpy(name,na); cout<<"Constructing\n"; }Student(Student&s) { num=s.num; strcpy(name,); score=s.score; cout<<"Copying\n"; } ~Student() { cout<<"Destructing\n"; }private: longnum; charname[20]; intscore;};voidfunc1(Studentst2){}Studentfunc2(){Studentst3;returnst3;}intmain(){ Studentst(1001,"Zhang",80),st1(st); //func1(st);//func2(); return0;}例3-6源代码
本章知识点:1.静态成员2.友元3.函数模板与类模板*面向对象程序设计知识点的回顾1.类的定义(1)类中的数据成员实现了不同类型相互关联数据的组合,数据成员为私有或保护,使类外函数不能访问,数据安全性得到保证。(2)所有对数据成员操作的函数均封装到类中,一旦数据出错只需检查成员函数即可。(3)类实现了数据与操作数据函数的组合2.对象定义及调用成员函数3.公共数据的存放及处理*静态成员1.静态数据成员声明时以static开头的数据成员称为静态数据成员。其特点(与非静态成员的区别):(1)定义对象时不会为静态数据成员分配内存空间。(2)静态数据成员是类中对象共享的数据,为所有类对象共同拥有,相当于类中的全局变量。图4-1User类中静态数据成员count示意图(3)静态数据成员具有静态生存期,必须在类外对其进行初始化,静态数据成员初始化的格式如下:数据类型类名::静态数据成员名=初始值;注意:静态数据成员在类外初始化时,不需要加关键字static。(4)静态数据成员的作用域为类的作用域,当静态数据成员是私有或受保护的时,在类外只能通过公有的成员函数进行访问,与全局变量相比,静态数据成员保证了数据的隐藏性和安全性。例4_1学生类中的静态数据成员#include<iostream>#include<string>usingnamespacestd;classStudent{public: Student(longnu=0,charna[]="noname",ints=0):num(nu),score(s) {strcpy(name,na);cout<<"Constructing\n"; }Student(Student&s) { num=s.num; strcpy(name,); score=s.score; cout<<"Copying\n"; } voidsetcount() { count++; cout<<count<<endl; } ~Student() { cout<<"Destructing\n"; }private: longnum; charname[20]; intscore; staticintcount;};intStudent::count=0;intmain(){ Studentst1(1001,"Zhang",80),st2(1002,"Li",85); cout<<sizeof(st1)<<endl;//定义对象时不为静态成员分配内存 st1.setcount();//对象st1、st2共享静态数据成员count st2.setcount(); return0;}2.静态成员函数未定义对象时如何输出静态数据成员的值,C++提供了静态成员函数,可以通过类名调用静态成员函数,静态成员函数的特点:(1)声明时加static(2)静态成员函数可以通过对象名调用,也可以通过类名调用(3)静态成员函数没有this指针,不能直接访问非静态成员,只能间接访问(举例说明)(4)静态成员函数一般只访问静态数据成员例4_2在学生类中增加静态数据成员学生人数count,在类外为其赋初值0,当增加一个学生时,count值加1,减少一个学生时,count值减1。增加静态成员函数showcount,输出count的值。增加复制构造函数,实现深复制。例4_3在下列程序的人员类Person中,增加静态数据成员num,在类外给num赋初值,使得运行结果为:num=52num=50#include<iostream>usingnamespacestd;classPerson{public: Person(){num++;} staticvoidshow(){cout<<"num="<<num<<endl; } ~Person(){num-=2;}private:};intmain(){Personper1;Person*ptr=newPerson;per1.show(); deleteptr;Person::show();return0;}注意:在类外定义静态成员函数时,不要加static,否则编译出错。*友元1.友元函数(1)普通函数声明为友元函数例4_4定义矩形类,数据成员包括矩形的长和宽,定义友元函数,求两矩形面积之和。(2)其它类的成员函数声明为友元函数例4_5定义学生类,包括学号、姓名和分数,定义教师类,包括可以修改学生分数的成员函数,该函数为学生类的友元函数。注意:教师类在学生类前定义,教师类的成员函数changescore在学生类定义之后定义。教师类定义前需要加:classStudent;//类的提前引用声明(3)友元函数提高了程序的运行效率,但破坏了类的封装性,使用时应权衡利弊。2.友元类友元不仅可以是函数,还可以是类。在一个类中声明另一个类,前面加上修饰符friend,被声明的类称为友元类,友元类中的所有成员函数都是另一个类的友元函数,可以直接访问该类中私有的和受保护的成员。(1)友元类声明(2)友元类的所有成员函数均为友元函数(3)友元是单向的,友元关系不能传递如A类是B类的友元,但不等于说B类也是A类的友员如A类是B类的友元,B类是C类的友元,但不等于说A类也是C类的友员*函数模板与类模板1.函数模板(1)函数模板的概念函数模板是一组相关函数的模型或样板。这些相关函数功能与代码相似,只是处理的数据类型不同,将这些相关函数合并成一个函数,处理的数据类型用通用类型符(即模板参数)代替,称作函数模板。(2)函数模板的定义例4_6求两个数中的较大值和较小值intfindmax(inta,intb)//只能求两个整数的较大值{returna>b?a:b;}改为函数模板,即将int用模板参数T代替,并对T进行说明template<typenameT>//模板参数说明Tfindmax(Ta,Tb){returna>b?a:b;}(3)函数模板的调用调用函数模板时,编译系统根据调用时使用的数据类型生成相应的具体函数,这一过程称作函数模板的实例化,生成的具体函数称作模板函数。调用函数模板的格式有两种,第一种与调用普通函数的格式相同,即:函数名(实参1,实参2,……);例如:findmax(5,6);或:findmax(5.5,6.6);第二种需要给出模板实参,即:函数名<模板实参1,模板实参2,…>(实参1,实参2,……);例如:findmax<double>(5.5,6);以上调用函数模板的语句中两个参数类型不同,编译时出错,需要用模板实参说明T的类型。注意:(1)typename可以用class代替(2)函数模板定义可以放在主函数后面,在主函数之前则给出函数模板声明,函数模板的声明和定义的前面都要给出模板参数声明。(3)模板参数可以有多个(4)当由实参1,实参2,……,即可确定函数模板中模板参数的类型时,就可以采用第一种方式调用函数模板。当由实参1,实参2,……,不能确定函数模板中模板参数的类型时,就要采用第二种方式调用函数模板。例4-7定义函数模板,求两个数的平均值#include<iostream>usingnamespacestd;template<typenameT1,typenameT2>T2findaver(T1a,T1b);intmain(){ cout<<findaver<int,float>(5,6)<<endl; return0;}template<typenameT1,typenameT2>T2findaver(T1a,T1b){ return(a+b)/2.0;}例4-8定义函数模板,在一维数组中查找给定的数,若存在,返回对应元素下标,若不存在,返回-1。#include<iostream>usingnamespacestd;template<typenameT1,typenameT2>//模板参数说明intfindnum(T1array[],intn,T2num)//函数模板中可以有多个模板参数{ for(inti=0;i<n;i++) if(array[i]==num) returni;return-1;}intmain(){ inta[]={1,4,7,200,0,-34,9,10},index1;index1=findnum(a,sizeof(a)/sizeof(int),-34);cout<<"index1="<<index1<<endl;doubleb[]={1.2,3.4,78.9,0,-100,5.6,7.8},index2;index2=findnum(b,sizeof(b)/sizeof(double),-100);cout<<"index2="<<index2<<endl;return0;}程序运行结果:index1=5index2=4程序说明:模板参数可以有多个。例4-9定义函数模板,求一维数组中元素的平均值。#include<iostream>usingnamespacestd;template<typenameT1,typenameT2>//模板参数说明T2findaver(T1array[],intn)//函数模板中模板参数有多个{ T1sum=0; T2aver;for(inti=0;i<n;i++) sum+=array[i]; aver=sum/(double)n;returnaver;}intmain(){doubleb[]={1,2,3,4,5,6,7,8},average;average=findaver<double,double>(b,sizeof(b)/sizeof(double)); cout<<"average="<<average<<endl; average=findaver<double,int>(b,sizeof(b)/sizeof(double)); cout<<"average="<<average<<endl;return0;}程序运行结果:average=4.5average=4程序说明:=1\*GB2⑴当由实参b,sizeof(b)/sizeof(double)不能确定函数模板中模板参数T1、T2的类型时,调用函数模板时要用findaver<double,double>形式告知编译系统,T1、T2的类型是double、double,据此生成相应的模板函数。=2\*GB2⑵调用函数模板时,T1、T2可以是不同类型,运行结果也会不同。2.类模板对于功能相同而仅仅是数据类型不同的两个或多个类,可以声明一个通用的类模板,可以有一个或多个通用的类型参数(模板参数)。例4-10定义一个类求两个数中的较大值和较小值#include<iostream>usingnamespacestd;classFind//求两个整数最大最小值的类{public:Find(intaa,intbb):a(aa),b(bb) { } intgetmax()//求最大值 { returna>b?a:b;} intgetmin()//求最小值 { returna<b?a:b; }private:inta,b;};intmain(){ intx,y;cin>>x>>y; Findf(x,y); cout<<"max="<<f.getmax()<<endl; cout<<"min="<<f.getmin()<<endl; return0;}类模板是一组相关类的模型或样板。这些相关类的功能与代码相似,只是处理的数据类型不同,将这些相关类合并成一个类,处理的数据类型用通用类型符(模板参数)代替,即是类模板。(1)类模板的定义例4-10中类改写为类模板#include<iostream>usingnamespacestd;template<classT>//声明模板参数也可以写为template<typenameT>classFind{public:Find(Taa,Tbb):a(aa),b(bb) { } Tgetmax()//求最大值 { returna>b?a:b;} Tgetmin()//求最小值 { returna<b?a:b; }private:Ta,b;};(2)类模板的使用类模板定义后,即可用类模板定义对象,定义类模板对象的格式是:类模板名<模板实参表>对象名;或者:类模板名<模板实参表>对象名(构造函数实参表);定义类模板对象时,编译系统根据定义对象时给出的模板实参,确定类模板中模板参数的类型,从而生成相应的类,这一过程称作类模板的实例化,生成的类称作模板类。例4-10中主函数代码改写如下:intmain(){ intx,y;cin>>x>>y; Find<int>f(x,y); cout<<"max="<<f.getmax()<<endl; cout<<"min="<<f.getmin()<<endl; return0;}(3)类模板的成员函数在类外定义改写上例。例如:template<classT>TFind<T>::getmin()//求最小值{ returna<b?a:b; }(4)可以为模板参数的最后若干个参数设置默认值。例4-11有多个模板参数的类模板#include<iostream>usingnamespacestd;template<classT1,classT2=float>//声明模板参数也可以写为template<typenameT>classFind{public:Find(T1aa,T1bb):a(aa),b(bb) { } T1getmax()//求最大值 { returna>b?a:b;} T1getmin()//求最小值 { returna<b?a:b; }T2getaver() {return(a+b)/2.0;}private:T1a,b;};intmain(){ intx,y;cin>>x>>y; Find<int,int>f1(x,y); cout<<"max="<<f1.getmax()<<endl; cout<<"min="<<f1.getmin()<<endl; cout<<"aver"<<f1.getaver()<<endl;Find<int>f2(x,y); cout<<"max="<<f2.getmax()<<endl; cout<<"min="<<f2.getmin()<<endl; cout<<"aver"<<f2.getaver()<<endl; return0;}程序说明:=1\*GB3①类模板的两个模板参数T1、T2中,最后一个模板参数T2有默认值。=2\*GB3②定义类模板对象f1时,给出两个模板参数,编译系统根据模板参数的值确定类模板中T1、T2的类型分别为int、int,生成具体的模板类。=3\*GB3③定义类模板对象f2时,只给出一个模板参数,另一个模板参数取默认值float,编译系统根据模板参数的值确定类模板中T1、T2的类型分别为int、float,生成具体的模板类。作业题:4-9编写程序,声明一个Cat类,拥有静态数据成员HowManyCats,记录Cat类对象的个数,并且拥有静态成员函数GetHowMany(),输出HowManyCats的值。4-11阅读下面的程序。⑴改正程序中的错误。#include<iostream>usingnamespacestd;classCat;voidSetValue(Cat&,int);voidSetValue(Cat&,int,int);classCat{public: friendvoidSetValue(Cat&,int);private: intweight; intage;};voidSetValue(Cat&ta,intw){ ta.weight=w;}voidSetValue(Cat&ta,intw,inta){ ta.weight=w; ta.age=a;}intmain(){ Catmimi; SetValue(mimi,3); SetValue(mimi,5,4);return0;}⑵将上面程序中的友元函数改为普通函数,并在Cat类中增加成员函数访问Cat类中私有的数据成员。
本章知识点:1.运算符重载的基本概念2.运算符重载的方法:成员函数形式与友元函数形式3.常用单目、双目运算符的重载4.流输出运算符与流输入运算符的重载5.字符串类*运算符重载的概念1.运算符重载的概念:对于已有的运算符进行定义,使该运算符不仅用于基本数据类型运算,也可用于类对象的运算2.运算符重载的意义:使程序可读性好。*运算符重载的方法1.友元函数形式(对于有的版本的VC++6.0,需要用老的.h头文件)2.成员函数形式3.两种形式的比较例5_1声明复数类,数据成员为复数的实部与虚部,重载加法运算符实现两个复数类对象的加法运算(两种形式实现)。注意:符号位显示问题*常用运算符的重载1.单目运算符的重载注意:C++约定,在自增(自减)运算符重载函数中,增加一个int形参,即为后增(后减)运算符函数。2.双目运算符的重载例5_2声明一个点Point类,包含点的水平坐标x、垂直坐标y两个数据成员及构造函数,显示点的坐标值的show函数,要求:(1)重载减法运算符(-)以实现Point类对象的减法运算;(2)重载前增量运算符(++)以实现Point类对象的x、y值各加1的运算(如需连续自增,则需返回Point对象引用);(3)重载后增量运算符(++)以实现Point类对象的x、y值各加1的运算;例5_3根据下面给出的人民币类RMB的声明,要求:(1)重载加法运算符+,实现RMB类对象的加法运算;(2)重载前增量与后增量运算符++,实现RMB类对象数据成员值加1角运算;(3)重载>、>=、==、!=运算符,实现RMB类对象的比较;#include<iostream>usingnamespacestd;classRMB{public:RMB(inty=0,intj=0):yuan(y),jiao(j){}voidshow(){cout<<yuan<<"元"<<jiao<<"角"<<endl;private:intyuan,jiao;};intmain(){RMBr1(6,7),r2(9,2),r3,r4;r3=r1+r2;r4=++r3;r4.show();r4=r3++;r3.show();r4.show();return0;}注意:角的取值在0~9之间。*运算符重载的规则(1)不能定义新的运算符(2)C++不允许重载的5个运算符:.(成员访问运算符)、.*(成员指针访问运算符)、::(域运算符)、sizeof(长度运算符)、?:(条件运算符)。(3)不能改变操作数个数(4)不能改变优先级(5)不能改变结合性(6)参数不能有默认值(7)参数至少有一个是类对象或对象引用(8)“=”、“&”运算符一般不需要重载,由系统提供,但当需要深拷贝时需自己定义赋值运算符(9)重载运算符应使其功能与其原有功能类似例5-4浅赋值与深赋值#include<iostream>usingnamespacestd;classStudent{public:Student(intn=0,char*na="noname",ints=0)//构造函数,{ no=n;name=newchar[20];//name指向堆中分配的一空间 strcpy(name,na); score=s;}voidset(intn,char*na,ints){no=n; strcpy(name,na); score=s; } voidshow(){cout<<no<<""<<name<<""<<score<<endl; }private: intno; char*name;intscore;};intmain(){Studentst1(1001,"Wang",80),st2;st2=st1;//调用默认赋值运算符重载函数,即执行st2.operator=(st1);cout<<"st2后:\n"; cout<<"st1:";st1.show(); cout<<"st2:";st2.show(); st2.set(1002,"Li",90);//修改st2的值 cout<<"\n修改st2的值后:\n"; cout<<"st1:";st1.show(); cout<<"st2:";st2.show(); return0;}程序运行结果:执行st2=st1后:st1:1001Wang80st2:1001Wang80修改st2的值后:st1:1001Li80st2:1002Li90程序说明:⑴由运行结果可知,当修改st2对象name的值时,使得st1对象name的值也改变了。出现这种情况的原因是:赋值前st1与st2对象的name指针指向不同的内存空间,执行赋值语句st2=st1时,调用了默认的赋值运算符重载函数,该函数代码如下:Student&operator=(constStudent&s){ no=s.no;name=;//name与指向同一内存空间 score=s.score; return*this;//返回当前对象}由于默认的赋值运算符重载函数实现的是浅赋值,使得=,即对象st2的name指针指向了对象st1的name指针对应的内存空间。因此,在执行语句st2.set(1002,"Li",90);修改st2数据成员的值时,使得st1中name的数据被修改了,因此在执行st1.show();语句时,输出的st1对象name为Li。⑵为了解决这个问题,需要自定义赋值运算符重载函数实现深赋值。注意:赋值运算符重载函数只能采用成员函数形式,因此修改后的代码如下:#include<iostream>usingnamespacestd;classStudent{public:Student(intn=0,char*na="noname",ints=0)//构造函数,{ no=n;name=newchar[20];//name指向堆中分配的一空间 strcpy(name,na); score=s;}voidset(intn,char*na,ints){no=n; strcpy(name,na); score=s; } voidshow(){cout<<no<<""<<name<<""<<score<<endl; }~Student()//析构函数,释放动态分配的空间{if(name!=NULL){delete[]name;} } Student&operator=(constStudent&s)//实现深赋值运算的重载函数 { no=s.no;strcpy(name,);//name仍指向原来的内存空间 score=s.score; return*this;//返回当前对象 }private: intno; char*name;intscore;};intmain(){Studentst1(1001,"Wang",80),st2; st2=st1;//调用自定义赋值运算符重载函数即执行st2.operator=(st1) cout<<"执行st2=st1后:\n"; cout<<"st1:";st1.show(); cout<<"st2:";st2.show(); st2.set(1002,"Li",90); cout<<"\n修改st2的值后:\n"; cout<<"st1:";st1.show(); cout<<"st2:";st2.show(); return0;}程序运行结果:执行st2=st1后:st1:1001Wang80st2:1001Wang80修改st2的值后:st1:1001Wang80st2:1002Li90程序说明:由输出结果可知,调用自定义赋值运算符重载函数,不会使st1、st2对象的name指针指向同一内存空间。*流输出运算符与流输入运算符的重载1.<<运算符的重载2.>>运算符的重载例ex5_5改写例ex5_1,重载<<与>>运算符,使得:能够输出及输入Complex对象例ex5_6根据下面给出的时间类Time的声明,要求:(1)重载增量运算符++,实现Time类对象加1分钟的运算;(2)重载流提取运算符>>,实现Time类对象数据的输入;(3)重载流插入运算符<<,使得能以“时:分”格式输出Time类对象数据成员的值。#include<iostream>usingnamespacestd;classTime{public:Time(inth=0,intm=0):hour(h),minute(m){}private:inthour,minute;};intmain(){Timenoon;cin>>noon;++noon;cout<<noon<<endl;return0;}注意:赋值运算符必须重载为成员函数形式,<<与>>运算符必须重载为友元函数形式*不同类型数据间的转换1.转换构造函数:与加法运算符重载函数(参数为复数对象)结合,实现复数类对象与双精度数的运算(转换为复数对象进行运算)。#include<iostream>#include<iomanip>usingnamespacestd;classComplex{public:Complex(doubler,doublei):real(r),imag(i){}Complex(doubler):real(r),imag(0){}voidshow(){ cout<<resetiosflags(ios::showpos)<<real<<setiosflags(ios::showpos)<<imag<<"i"<<endl;}friendComplexoperator+(Complexrr1,Complexrr2);private:doublereal,imag;};Complexoperator+(Complexcc1,Complexcc2){returnComplex(cc1.real+cc2.real,cc1.imag+cc2.imag);}intmain(){ Complexc1(3,5),c2(4.1,10),c3(0,0);c3=c2+10;//或者c3=10+c2;c3.show();return0;}2.强制类型转换运算符的重载:在未定义加法运算符重载函数前提下,实现复数类对象与双精度数的运算(转换为双精度数进行运算)。#include<iostream>#include<iomanip>usingnamespacestd;classComplex{public:Complex(doubler,doublei):real(r),imag(i){}Complex(doubler):real(r),imag(0){}operatordouble(){returnreal;}voidshow(){ cout<<resetiosflags(ios::showpos)<<real<<setiosflags(ios::showpos)<<imag<<"i"<<endl;}//friendComplexoperator+(Complexrr1,Complexrr2);private:doublereal,imag;};/*Complexoperator+(Complexcc1,Complexcc2){ returnComplex(cc1.real+cc2.real,cc1.imag+cc2.imag);}*/intmain(){ Complexc1(3,5),c2(4.1,10),c3(0,0);c3=10+c2;//或者c3=c2+10;c3转换为double加上10,等于13,再转换为Complex给c3c3.show();return0;}*string类1.字符串类简介使用字符串类需要包含新的头文件。2.字符串类对象的赋值与连接3.字符串类对象的输入输出4.字符串类对象的运算5.字符串类的特性string类classstring{ char*p; intsize;public:string();//无参构造函数声明 string(char*str);//有1个参数的构造函数声明 string(conststring&o);//复制构造函数 ~string(){deletep;}//析构函数 friendostream&operator<<(ostream&stream,string&o);//重载流输出运算符函数声明 friendistream&operator>>(istream&stream,string&o);//重载流输入运算符函数声明 String&operator=(string&o);//重载赋值运算符声明String&operator=(char*s);//重载赋值运算符声明 stringoperator+(string&o);//重载加法运算符声明stringoperator+(char*s);//重载加法运算符声明 friendstringoperator+(char*s,string&o);//重载加法运算符声明char&operator[](inti);//重载下标运算符声明constchar&operator[](inti)const;//重载下标运算符声明//以下是重载比较运算符的函数定义 intoperator==(string&o){return!strcmp(p,o.p);} intoperator!=(string&o){returnstrcmp(p,o.p);} intoperator<(string&o){returnstrcmp(p,o.p)<0;} intoperator>(string&o){returnstrcmp(p,o.p)>0;}intoperator<=(s
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 审计局出差日常管理制度
- 审计局学法用法方面制度
- 审计局收发工作制度
- 审计局离岗告知制度
- 审计工作查根追责制度
- 审计廉政监督员制度
- 审计报告信息公开制度
- 审计整改及资料归档制度
- 审计机关财务管理制度
- 审计独立核查制度
- 2025年度民办非企业单位工作计划
- 《植物生产与环境》考试复习题库
- 【八年级上册地理】一课一练2.2 世界的气候类型 同步练习
- 大学生魅力讲话实操学习通超星期末考试答案章节答案2024年
- 《游园》课件统编版高中语文必修下册
- DB46 T 192-2010 麒麟菜栽培技术规程
- 【盒马鲜生冷供应链物流成本现状、问题及优化建议探析11000字(论文)】
- HG/T 22820-2024 化工安全仪表系统工程设计规范(正式版)
- 基于人工智能的文化遗产保护与传承策略
- 2022年上海市养老服务综合统计监测报告
- 生物工程设备课件
评论
0/150
提交评论