资源预览内容
第1页 / 共204页
第2页 / 共204页
第3页 / 共204页
第4页 / 共204页
第5页 / 共204页
第6页 / 共204页
第7页 / 共204页
第8页 / 共204页
第9页 / 共204页
第10页 / 共204页
亲,该文档总共204页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
C+程序设计第1章 C与C+不同本章主要内容数据类型不同动态内存分配机制不同输入输出机制函数不同常变量面向过程与面向对象思想的区别1.1数据类型不同C+语言的基本数据类型与C语言一致,可以在C+中直接应用。不同之处: C语言整型占2个字节,而C+中基本整型占4个字节。 C语言指针类型占2个字节,而C+中指针类型占4个字节。 C+中结构体类型 类型名省略关键字struct1.2 动态内存分配机制不同C语言利用动态内存分配函数 malloc() calloc() free()C+利用动态分配和撤销内存的运算符 new和delete 注意: new和delete是运算符,不是函数, 执行效率高。new和delete运算符new运算符使用的一般格式 new 类型初值; 例如: new int; new int(100); new char10; float *p=new float(3.14);delete运算符使用的一般格式 delete 指针变量; 例如:delete p; delete pt;1.3 C与C+不同的输入输出机制C+中输入输出流的基本概念使用输出流对象cout的基本方法和格式使用输入流对象cin的基本方法和格式C+中输入输出流的基本概念C+ 中通过调用输入输出流库中的流对象cin和cout实现输入输出。cout是输出流对象的名字,cin是输入流对象的名字是流提取运算符,作用是从默认的输入设备的输入流中提取若干字节送到内存区中指定变量。C+中输入输出流的基本操作使用cin, cout和流运算符必须包含头文件 #includecout语句的一般格式为 cout表达式1表达式2变量1变量2变量n; 注意:不能使用cin语句输入空格和回车字符 必须输入时使用getchar()函数输入1.4 C与C+函数不同内联函数函数重载带默认参数的函数引用和引用做函数参数一内联函数编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去。这种嵌入到主调函数中的函数称为内联函数。指定内联函数,在函数首行的左端加关键字inline即可。内联函数中不能包含复杂的控制语句,如循环语句等。 #includeinline int max(int a,int b,int c);int main() int i=10,j=20,k=30,m; m=max(i,j,k); coutmax=ma) a=b; if(ca) a=c; return a;二函数重载C+允许用同一函数名定义多个函数,这些函数的参数类型或者参数个数不同,这就是函数的重载。重载函数的参数个数,参数类型,或参数顺序3者中必须至少有一种不同,函数返回值类型可以相同也可以不同。所谓重载就是“一物多用”,不仅函数可以重载,运算符可以重载。 #includeint max(int a,int b,int c); int max(int a,int b);int main() int a=8,b=20,c=30; coutmax(a,b,c)=max(a,b,c)endl; coutmax(a,b)=max(a,b)a) a=b; if(ca) a=c; return a; int max(int a,int b) if (ab) return a; else return b; 三带默认参数的函数当用同样实参多次调用同一函数时,C+中可以给形参一个默认值,这样形参就不必一定从实参取值了。例如:函数声明 float area(float r=6.5); 则函数调用 area(); area(7.5);必须在函数调用之前将默认值的信息通知编译系统三带默认参数的函数实参与形参结合是从左至右。因此指定默认值的参数必须放在形参表列最右端。 例如:void f1(float a, int b=0, int c) ? void f2(float a,int b,int c=0)一个函数不能既作为函数重载,有作为有默认参数的函数,否则产生二义性。 例如:有函数声明int max(int a,int b,int c=10); int max(int a,int b); 函数调用max(5,23);调用哪个函数?#includeint max(int a,int b,int c=0);int main() int a,b,c; cinabc; coutmax(a,b,c)= max(a,b,c)endl; coutmax(a,b)=max(a,b)a) a=b; if (ca) a=c; return a;四引用引用的作用是为变量起一个别名。 例如: int a; int &b=a; 说明b是a的引用,即b是a的别名。 b和a占内存中的同一个存储单元。在声明一个引用时,必须同时使之初始化,即声明它代表的变量,不能在作为其他变量的引用。 例如:int a1,a2; int &b=a1; int &b=a2; /正确吗?#include#includeint main() int a=10; int &b=a; a=a*a; coutasetw(6)bendl; b=b/5; coutb setw(6)aendl; return 0;例.引用和变量的关系引用做函数参数C+引入引用的主要用途是传递函数参数,以扩充函数传递数据的功能。目前我们学习的参数传递方法有三种 变量名做参数 传递变量指针 传递变量的别名#includevoid swap(int &,int &);int main() int i=3,j=5; swap(i,j); couti= ij= jendl; return 0;void swap(int & a,int & b) int temp; temp=a; a=b; b=temp;例.利用“引用形参”实现两个变量值的互换1.5 C+中与常变量相关的概念常变量概念和特点常变量作函数参数指向常变量的指针变量一常变量概念和特点定义变量时,加上关键字const,则变量的值在程序运行期间不能改变,称为常变量 例如:const int a=3;定义常变量时必须同时对其初始化 例如:const int a; a=3; /错误,常变量不能被赋值二常变量作函数参数 用const限制函数的参数能保证函数不对参数做任何修改针对指针和引用参数,可以避免实参被意外修改int f(int i1,const int i2) i1+; i2+; /正确吗? return i1+i2;三指向常变量的指针变量 定义指向常变量指针变量的一般形式: const 类型名 *指针变量名; 例如:const char *ptr;常变量只能用指向常变量的指针变量指向,不能用一般的指针变量指向 例如:const char c=“boy”; const char *p1; p1=c; char *p2=c; /正确吗? 指向常变量的指针变量 指向常变量的指针变量还可以指向非const 的变量,此时不能通过指针变量改变该变量的值。 例如:char c1=a; const char *p; p=&c1; *p=b; / ?如果函数形参是指向const型的指针变量,在函数执行过程中不能改变指针变量所指向的变量的值。1.6面向过程与面向对象思想的区别例如:求三角形周长和面积例.利用面向过程思想求三角形周长和面积#includestdio.h#includemath.hfloat mianji(float a,float b,float c) float s,area; s=(a+b+c)/2.0; area=sqrt(s*(s-a)*(s-b)*(s-c); return(area); float zhouchang(float a,float b,float c) return(a+b+c); void main() float a,b,c,m,z; scanf(%f,%f,%f,&a,&b,&c); m=mianji(a,b,c); z=zhouchang(a,b,c); printf(%f,%f,m,z);1.面向过程编程思想将数据和处理数据的过程分离为相互独立的实体,当数据结构发生改变时,所有的相关处理过程都要发生改变。结构化程序设计的基本思想:自顶向下、逐步求精,其程序结构按照功能划分为若干基本模块,形成一个树状结构;每个模块内部均是由顺序、选择、循环三种基本结构组成;其模块实现的基本方法是子程序(函数)。程序=数据结构+算法例.利用面向对象思想求三角形周长和面积#include#includemath.husing namespace std;class trangle public: trangle(float x ,float y,float z) a=x;b=y;c=z; float zhouchang() return a+b+c; float mianji() return(sqrt(a+b+c)/2.0)*(a+b+c)/2.0)- a)*(a+b+c)/2.0)-b)*(a+b+c)/2.0)-c); private: float a,b,c; int main() trangle t1(3,4,5),t2(6,8,12); coutt1.zhouchang()endlt2.mianji()实数的加法 -复数的加法实验一 C+初步实验目的: C+流输入输出 C+中函数的基本概念实验内容利用函数重载求任意二个数中的大数。利用引用做函数参数对3个变量按由小到大的顺序排序利用函数默认参数求3个数最大值,验证定义默认参数和函数重载后果。C+程序设计第2章 类与对象本章主要内容结构体与类类的定义方法C+类成员的访问属性类和对象的简单应用举例构造函数与析构函数对象指针类的概念:C+中的类就是一种用户自定义的数据类型,和其他数据类型不同的是,组成这种类型的不仅可以有数据,而且可以有对数据进行操作的函数,它们分别叫做类的数据成员和类的函数成员。定义性说明和引用性说明,定义性说明就是定义类,说明了类的成员。而引用性说明就是只说明了类名,留待后面的代码对这个类加以定义。 2.1类的声明和对象的定义一、结构体与类结构体struct Studentint num; char name20; float score3; ;C+中允许用中允许用struct来定义一来定义一个个类类类型类型Student 是一个是一个合法的合法的类类类型类型一个完整的类定义包括关键字class 类名 类体 在类体中定义类的属性(类中的数据成员的取值范围)和操作(类中的成员函数名)。class 类名 public: 若干成员; protected: 若干成员; private: 若干成员; ;二、类的定义类成员访问限定符public的成员:一般是成员函数,用于定义类的外部接口,在程序中的任何部分都可访问。private的成员:一般是数据成员,用于描述类的属性,它们只能被类自身的成员函数访问,类的成员默认情况下是私有的。protected的成员:不能被类外访问,但可被派生类的成员函数访问。注意: 关键字 private public protected的出现顺序是任意的,并且可以不出现或出现多次。但类中的每个成员只能有一种特定的访问属性Class Cylinder public : void setcylinder(double r,double h); double getradius(); double getheight(); double volume(); double surface_area(); private: double radius; double height;注:在类内不允许对声明的数据成员进行初始化举例类是用户定义的一种类型,在程序中我们根据该类的类型说明一个变量,那么这个变量就是一个对象,它是具体的,在内存中对象被分配相应的内存,它是以类作为样板生成的。事实上建立对象时,只有每个对象的数据是分别占用内存的,而操作这些数据的代码(函数)只有一份,由各对象共享,这是编译器实现对象的一种方法而已,我们仍应理解为一个对象是独立的由数据和代码组成。三、类和对象的定义 与一般变量相同,对象也必须经过声明后才能使用,声明类的对象如下: 类名 对象名;例如:Cylinder cylinder1,cylinder2;Cylinder cylinder10;对象的声明:定义类的成员函数的实现的一般形式为: 返回类型 类名:成员函数名(参数说明) 函数体 “:”是作用域运算符,它标明所要定义的函数属于哪个类注:1、如果函数体的定义放在类的声明内,这样定义的函 数自动成为内联函数 2、如果在类外定义一个类的内联成员函数,应该在函 数类型之前用关键字inline进行说明 四、类的成员函数的定义 void Cylinder:setcylinder(double r,double h) radius=r;height=h; double Cylinder:getradius() return radius; double Cylinder:getheight() return height; double Cylinder:volume() double vol; vol=3.1415926*radius*radius*height; return vol;例2.12.2 C+类成员的访问控制 引用方式 (1)对象名.数据成员名 、对象名.成员函数名(参数表) (2) 指向该类对象的指针-成员名 (3)对象的引用.成员名 在C+中,数据封装是通过类来实现的。由于类中成员指定了访问权限,所以程序中其他函数就不能访问对象的私有成员,只能通过公有成员提供接口来访问 class A public: int x; setX(int a) x=a; getX() return x; ; A object; object.x=5;类的数据成员可以是类类型,也就是说类的声明中数据成员可以是另一个类的对象,但必须注意以下两点: 这个对象不能是本类的对象。 作为数据成员的对象所属的类,应该在声明这个对象之前进行声明class A . ;class B A a; .;2.3类和对象的简单应用举例例2.2求三个长方体的体积。编写一个基于对象的程序,数据成员包括length、width、height。要求用成员函数实现以下功能:由键盘分别输入三个长方体的长,宽,高;计算长方体的体积;输出长方体的体积;int rectangle: calculate() return (length*width*heigth);void rectangle:output()coutcalculate(); int main()rectangle t1; t1.input(); t1.calculate(); t1. output(); return 0;#include using namespace std;class rectangle public: void input(); void output(); int calculate(); private: int length; int width; int heigth;void rectangle:input() cinlength; cinwidth; cinheigth;2.4构造函数与析构函数一、构造函数对象的初始化 将确定对象的初始状态,即对其成员的初始值进行确定。类的数据成员不能在声明类时初始化如果类中成员全部是public,可在定义对象时对数据成员进行初始化C+提供了构造函数(constructor)来处理对象的初始化问题构造函数举例例2.3void Time :show_time()couthour“:”minute“:”secendl;int main() Time t1; t1. set_time(); t1. show_time(); Time t2; t2. show_time(); return 0;#include using namespace std;class Time public: Time() hour=0; minute=0; sec=0; void set_time(); void show_time(); private: int hour; int minute; int sec;void Time:set_time() cinhour; cinminute; cinsec;1、构造函数的定义构造函数的函数名必须与类名相同。 是一个很特殊的成员函数,因此构造函数需要在类中说明(定义) ;构造函数没有返回值,也不需在定义时声明函数类型。构造函数的主要作用是完成对类对象的初始化工作。构造函数不能由编程人员显式的直接调用。在构造函数的函数体中不仅可以对数据成员赋初值,而且可以包含其他语句。在创建一个类的新对象的同时,系统会自动调用该类的构造函数为新对象初始化。2、带参数的构造函数采用带参数的构造函数,在调用不同对象的构造函数时,从外面将不同的数据传递给构造函数,以实现不同的初始化。构造函数首部的一般格式为构造函数名(类型 1 形参1,类型2 形参2,)实参是在定义对象时给出的。定义对象的一般格式为 类名 对象名(实参1,实参2,);带参数的构造函数举例例2.4int Box:volume() return (height*width*length);int main()Box box1(12,25,30); coutthe volume of box1 is box1.volume()endl;Box box2(15,30,21);coutthe volume of box2 is box2.volume()endl; return 0;#include using namespace std;class Box public: Box(int,int,int); int volume(); private: int height; int width; int length;Box:Box(int h,int w,int len) height=h; width=w; length=len;3、用参数初始化表对数据成员初始化C+还提供另一种初始化数据成员的方法参数初始化表来实现对数据成员的初始化。在原来函数首部的末尾加一个冒号,然后列出参数的初始化表这种写法方便、简练,尤其当需要初始化的数据成员较多时更显其优越性。 举例例2.5int Box:volume() return (height*width*length);int main()Box box1(12,25,30); coutthe volume of box1 is box1.volume()endl;Box box2(15,30,21);coutthe volume of box2 is box2.volume()endl; return 0;#include using namespace std;class Box public: Box(int h,int w,int len):height(h),width(w),length(len) int volume(); private: int height; int width; int length;4、构造函数的重载 在一个类中可以定义多个构造函数,以便对类对象提供不同的初始化的方法,供用户选用。这些构造函数具有相同的名字,而参数的个数或参数的类型不相同。这称为构造函数的重载。int Box:volume() return (height*width*length);int main()Box box1; coutthe volume of box1 is box1.volume()endl;Box box2(15,30,21);coutthe volume of box2 is box2.volume()endl; return 0;#include class Box public: Box(int h,int w,int len):height(h),width(w),length(len) Box(); int volume(); private: int height; int width; int length;Box:Box() height=10; width=10; length=10;说明:调用构造函数时不必给出实参的构造函数,称为默认构造函数(default constructor)。显然,无参的构造函数属于默认构造函数。一个类只能有一个默认构造函数。如果在建立对象时选用的是无参构造函数,应注意正确书写定义对象的语句。尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时只执行其中一个构造函数,并非每个构造函数都被执行。5、使用默认参数的构造函数构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使形参取默认值。说明:(1) 应该在声明构造函数时指定默认值。(2) 如果构造函数的全部参数都指定了默认值,则在定义对象时可以给一个或几个实参,也可以不给出实参。(3) 在一个类中定义了全部是默认参数的构造函数后,不能再定义重载构造函数。int Box:volume() return (height*width*length);int main()Box box1; coutthe volume of box1 is box1.volume()endl;Box box2(15);coutthe volume of box2 is box2.volume()endl;Box box3(15,30);coutthe volume of box2 is box3.volume()endl; return 0;#include class Box public: Box(int h=10,int w=10,int len=10); int volume(); private: int height; int width; int length;Box:Box(int h,int w,int len) height=h; width=w; length=len;6、构造函数小结如构造函数带有参数, 在声明对象时,就必须利用实参对对象进行初始化。如构造函数不带参数,定义对象时不能带括号。与普通函数一样,构造函数也可以定义为内联函数,可以带默认形参值,也可以重载。注:当类中没有声明构造函数时,系统会为之生成一个不带参数的构造函数,该函数体内没有任何语句,不执行任何操作。二、析构函数析构函数:和构造函数作用相反的成员函数。函数首:类名()主要功能:释放对象的内存资源。析构函数的作用并不仅限于释放资源方面,它还可以被用来执行“用户希望在最后一次使用对象之后所执行的任何操作”。析构函数无返回值、无函数类型、无参数。一个类可以有多个构造函数,但只能有一个析构函数系统调用构造和析构函数的顺序:先构造的后析构,后构造的先析构析构函数调用的条件如果在一个函数中定义了一个对象,当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。如果定义了一个全局对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。2.5对象指针一、指向对象的指针:存放对象指针的指针变量定义形式:类名 *对象指针名; 例:Box *p,box1; p=&box1;通过对象指针访问对象成员 例:*p box1 (*p).width box1.width p-width (*p). volume() box1. volume() p-volume()二、指向对象成员的指针指向对象数据成员的指针指向对象成员函数的指针this 指针 1、指向对象数据成员的指针指向对象数据成员的指针变量概念定义形式: 数据成员类型名 *指针变量名访问形式与指向变量的指针变量的使用完全相同2、指向对象成员函数的指针与指向普通函数的指针变量的定义方法不同 复习:数据类型名(*指针变量名)(参数表列);定义形式:数据类型名(类名:*指针变量名)(参数表列);确定指向关系 指针变量名=&类名:成员函数名举例: float (Box:*p1)( ); p1=&Box:volume; . (box1.*p1)(); 3、this 指针this指针:为解决不同对象调用同一个函数段时如何引用不同的数据成员的问题。C+系统提供的,每一个成员函数都隐式包含的特殊指针。是指向本类对象的指针,值是当前被调用的成员函数所在对象的地址。隐式使用,作为参数传递给成员函数的 例:box1.volume( ); C+系统处理为:box1.volume(&box1); return(this-length*this-width*this-height)this=&box1实验二 类与对象构造一个圆类Circle,属性为半径radius、求圆周长和面积,实现根据输入的半径计算周长和面积并输出。要求定义以半径为参数、缺省值为0的构造函数。构造一个矩形类Rectangle,数据成员为矩形的左下角与右上角的坐标,利用成员函数实现对矩形周长与面积的计算。 C+程序设计第3章 数据的共享与保护本章主要内容共用数据的保护对象的赋值与复制静态成员友元3.1共用数据的保护共用数据的保护:为保证数据在一定范围内共享,同时又保证它不被任意修改一、常对象:定义一般形式 类名 const 对象名(实参表列); 或 const 类名 对象名(实参表列); 例:const Box box1(1,1,1);常对象的所有数据成员的值都不能被修改,也不能调用该对象的非const型的成员函数。二、常对象成员在声明类时将成员声明为const1.常数据成员const 类型 数据成员名;例:const float length;length 为常数据成员,它的值不能发生改变常数据成员只能通过构造函数的参数初始化表对常数据成员进行初始化。在类体中声明一个常数据成员后,该类所有对象此常数据成员的值都不能改变2.常成员函数声明形式:类型 函数名(参数表列) const;定义形式:类型 函数名(参数表列) const 函数体 常成员函数只能引用本类的数据成员,但不能修改数据成员常成员函数可以引用const数据成员,也可以引用非const数据成员;const数据成员可以被const成员函数引用,也可以被非const成员函数引用;三、指向对象的常指针定义一般形式: 类名 *const 指针变量名=对象地址;例: Box box1(1,1,1); Box *const p1=&box1;指针变量的指向关系不变,始终指向一个对象,但可以改变其所指对象中数据成员的值。常用来做函数的形参,目的是不允许在函数调用的过程中改变指针变量的值。四、指向常对象的指针变量1.指向常变量的指针变量定义一般形式: const 类型名 *指针变量;例:const int a; const int *pt=&a;说明:常变量只能用指向常变量的指针变量来指向指向常变量的指针变量还可以用来指向非const变量此时不能通过指针改变指针所指向变量的值2.指向常对象的指针变量定义形式:const 类名 *指针变量名;例读程改错void main() void fun(const Box *p); Box box1(2,3,6); fun(&box1);void fun(const Box *p) p-width=23; coutwidthendl;五、对象的常引用引用:变量或对象的引用就是变量或对象的别名。对象的常引用:const 类名 &引用对象名;常作函数的形参,保证在函数调用过程中不改变引用对象的值。3.2 对象的赋值对象的赋值: 一般形式:对象名1=对象名2;说明:对象的赋值只对其中的数据成员赋值,而不对成员函数赋值;类的数据成员中不能包括动态分配的数据。对象的复制一般形式:类名 对象2(对象1);例:Box box2(box1);含义:用box1对象复制出一个box2对象另一种复制形式: 类名 对象2=对象1;是建立新对象的一种方法用这种方法建立对象时要调用一个特殊的构造函数复制构造函数(copy constructor)复制构造函数功能:用一个已知的对象来初始化一个被创建的同类对象;特点函数名同类名,无返回值和函数类型;只有一个参数,是对某个对象的引用; :(const & )每个类都必须有一个复制构造函数;如果类中没有说明复制构造函数,则编译系统自动生成一个具有上述形式的缺省拷贝初始化构造函数,作为该类的公有成员;例class Boxpublic: Box(float=1,float=1,float=1); Box(const Box &b); void get_value(); float volume(); void display();private: float length; float width; float height;Box:Box(float len,float w,float h)length=len;width=w; height=h;Box:Box(const Box &b) length=b.length; width=b.width; height=b.height;void Box:get_value() cinlength; cinwidth; cinheight;float Box:volume() return(length*width*height);void Box:display() coutvolume()endl; void main() Box box1(10,12,30); cout“the volume of box1 is” box1. volume()endl; Box box2=box1; cout“the volume of box2 is” box2. volume()endl;调用复制构造函数的三种情况明确表示由一个对象复制另一个对象时;例如:Box box1(box2);当对象作为函数实参传递给函数形参时(传值调用);例如:fun(box1);当对象作为函数返回值时(数据值);例如:return box1;3.3 静态成员目的:解决数据共享问题,即不通过全局对象,而实现同类多个对象之间的数据共享。一、静态数据成员1、静态数据成员、静态数据成员是是类的所有对象共享类的所有对象共享的成员,而不是某个对象的成员,而不是某个对象的成员的成员;对多个对象来说,静态数据成员对多个对象来说,静态数据成员只存储在一个只存储在一个地方地方,供所有对象使用,供所有对象使用;静态数据成员的静态数据成员的值值对对每个对象每个对象都是都是一样一样的,并的,并且其值可以被任何一个对象更新;且其值可以被任何一个对象更新;2、使用方法与注意事项、使用方法与注意事项静态数据成员在静态数据成员在定义或说明定义或说明时前面加上关键字时前面加上关键字static;private:static int s;s是私有的静态数据成员;一、静态数据成员(续)静态数据成员是静态存储的,它是静态生存期,静态数据成员是静态存储的,它是静态生存期,必须对它进行初始化必须对它进行初始化;静态数据成员的初始化与一般数据成员初始化不静态数据成员的初始化与一般数据成员初始化不同,格式如下:同,格式如下:说明:说明:k初始化只能初始化只能在类体外在类体外进行进行,前面不加前面不加static,以免与一般静态变以免与一般静态变量或对象混淆;量或对象混淆; :=;k是在所有对象之外单独开辟内存空间的;是在所有对象之外单独开辟内存空间的;k在程序编译时分配内存空间,到程序结束时释放内存在程序编译时分配内存空间,到程序结束时释放内存;k既可通过对象名引用,又可通过类名引用。既可通过对象名引用,又可通过类名引用。一、静态数据成员(续)例分析下列程序的输出结果。#include class Myclasspublic: 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;void Myclass:GetNumber()coutNumber=A, B, Cendl;void Myclass:GetSum()coutSum=Sumendl;void main() Myclass M(3,7,10),N(14,9,11); M.GetNumber(); N.GetNumber(); M.GetSum(); N.GetSum();二、静态成员函数1、作用:、作用: 操作静态数据成员;操作静态数据成员;3、注意事项、注意事项静态成员函数的实现中静态成员函数的实现中不能不能直接直接引用引用本本类中的类中的非静态成员非静态成员,可以引用本可以引用本类中的类中的静态成员静态成员;静态成员函数中要静态成员函数中要引用非静态成员引用非静态成员时,可以时,可以通通过对象过对象来引用来引用;2、使用格式、使用格式 ( 在声明成员函数前加在声明成员函数前加static) 例如:例如: static int volume();静态成员函数和静态数据成员是类的一部分静态成员函数和静态数据成员是类的一部分;二、静态成员函数(续)例 分析下列程序的输出结果。#include class Mpublic: M(int a) A=a;B+=a; static void f1(M m);private: int A; static int B;void M:f1(M m) coutA=m.Aendl; coutB=Bendl;int M:B=0;void main() M P(5),Q(10); M:f1(P); M:f1(Q);A=5B=15A=10B=153.4友元2、友元的分类、友元的分类是一种是一种定义在类外部定义在类外部的的类或类或普通函数普通函数,但需要在,但需要在类体类体内进行说明内进行说明(前面加前面加friend关键字);关键字);友元类友元类;1、使用格式、使用格式友元函数友元函数;不是成员函数,但可以不是成员函数,但可以访问访问类中的类中的私有成员私有成员;一、友元函数例 分析下列程序的输出结果。#include class Timepublic: Time(int new_hours,int new_minutes) hours=new_hours; minutes=new_minutes; friend void Time12(Time time); friend void Time24(Time time);private: int hours,minutes;void Time12(Time time) if(time.hours12) time.hours-=12; couttime.hours:“time.minutes PMendl; else couttime.hours:“ time.minutes AMendl;void Time24(Time time)couttime.hours:“ time.minutesendl;void main() Time time1(20,30),time2(10,45); Time12(time1); Time24(time1); Time12(time2); Time24(time2); 8:30 PM20:3010:45 AM10:45友元成员函数二、友员类例分析下列程序的输出结果。#include class Xfriend class Y;public: void Set(int i) x=i; void Display() coutx=x, y=yendl;private: int x; static int y;class Ypublic: Y(int i,int j); void Display();private: X a; ;int X:y=1;Y:Y(int i,int j) a.x=i; X:y=j;void Y:Display()coutx=a.x, ; couty=X:yendl;void main() X b; b.Set(5); b.Display(); Y c(6,9); c.Display(); b.Display();实验二验证例题调试(实验指导书P145) 三调试(实验指导书P164) 3(1),3(2)C+语言程序设计第4章 类的继承与派生 本章主要内容类的继承与派生派生类成员的访问属性派生类的构造函数和析构函数多重继承 4.1类的继承与派生一、基本概念1. 派生与继承的意义继承性 是面向对象程序设计语言的基本特性之一,是指在已有类的基础上建立新的类。它允许在构造软件系统的层次结构中利用已存在类的部分或全部内容,从而大大提高了软件的重用性。派生类 它是继承的直接产物,它是通过继承已有类而产生的新类,是代码重用的具体实现。昆虫有翅无翅蛾苍蝇蝴蝶如图:昆虫分类2. 派生与继承的概念继承:在定义一个类A时,若它使用了一个已定义类B的部分或全部成员,则称类A继承了类B,并称类 B为基类或父类,称类A为派生类或子类。派生:在C+语言中,称一个类继承另一个类的过程为派生一个类。 3.基类和派生类n基类基类(父类父类) :已存在的用来派生新类的类;已存在的用来派生新类的类;n派生类派生类(子类子类):由已存在的类派生出的新类;:由已存在的类派生出的新类;n单继承单继承:从:从一个基类一个基类派生的继承;派生的继承;n多继承多继承:从:从多个基类多个基类派生的继承;派生的继承;基类派生类ABACB单继承多继承基类与派生类单继承与多继承二、派生类的定义格式class : ;1.单继承派生类的声明形式:二、派生类的定义格式(续) 2. 继承方式public:公有公有继承;继承;private:私有私有继承;继承;protected:保护保护继承;继承;作用:控制基类中声明的成员在多大的范作用:控制基类中声明的成员在多大的范围内能被派生类的用户访问围内能被派生类的用户访问;派生类私有成员公有成员保护成员私有成员公有成员保护成员基类部分新定义部分派生类成员派生类的构成三、派生类的构成构造派生类的过程:从基类接收成员;调整从基类接收的成员;声明派生类的新增成员;定义派生类的构造函数和析构函数。三、派生类的构成(续)四、基类与派生类的关系基类是对若干个派生类的抽象,而派生类是基基类是对若干个派生类的抽象,而派生类是基类的具体化;基类抽取了它的派生类的公共特征,类的具体化;基类抽取了它的派生类的公共特征,而派生类通过增加行为将抽象类变为某种有用的类而派生类通过增加行为将抽象类变为某种有用的类型。型。1、派生类是基类的具体化派生类是基类的具体化2、派生类是基类定义的延续派生类是基类定义的延续派生类将其自身与基类区别开来的方法是添加派生类将其自身与基类区别开来的方法是添加数据成员和成员函数;数据成员和成员函数;3、派生类是基类的组合派生类是基类的组合 4.2派生类成员的访问属性公有基类公有派生类特点:基类的公有成员和保护成员作为派生类的成员时,都保持原有的访问属性,而基类的私有成员仍然为基类私有。 公有继承公有继承(public)私有基类私有派生类特点:基类的公有成员和保护成员作为派生类的成员时,都作为派生类的私有成员,且不能被这一派生类的子类访问,而基类的私有成员仍然为基类私有。不能通过派生类对象引用从私有基类继承过来的任何成员。 4.2派生类成员的派生类成员的访问属性访问属性 (续续) 私有继承私有继承(private) 保护继承保护继承(protected) 4.2派生类成员的派生类成员的访问属性访问属性 (续续)保护成员:受保护的成员不能被类的外部访问,但可以被派生类成员函数访问保护继承保护基类保护派生类特点:基类的公有成员和保护成员作为派生类的成员时,都作为派生类的保护成员,而基类的私有成员仍然为基类私有.基基类的的访问特性特性类的的继承特性承特性子子类的的访问特性特性PublicProtectedPrivatePublicPublicProtectedNo accessPublicProtectedPrivateProtectedProtectedProtectedNo accessPublicProtectedPrivatePrivatePrivatePrivateNo access表:继承对基类成员的访问能力 4.2派生类成员的派生类成员的访问属性访问属性 (续续)类B为类A的直接派生类,类C为类A的间接派生类;类A是类B的直接基类, 是类C的间接基类;类C对类A的成员的访问属性,由类A成员的访问属性、类B的继承方式、类C的继承方式共同决定。 4.2派生类成员的派生类成员的访问属性访问属性 (续续) 多级派生的访问属性多级派生的访问属性类 A类 B类 C例分析下列程序中的访问权限。 4.2派生类成员的派生类成员的访问属性访问属性 (续续)class Locationpublic: void InitL(int xx,int yy); void Move(int xOff,int yOff); int GetX() return X; int GetY() return Y;private: int X,Y;void Location:InitL(int xx,int yy) X=xx; Y=yy;void Location:Move(int xOff,int yOff) X+=xOff; Y+=yOff;class Rectangle:public Locationpublic: void InitR(int x,int y,int w,int h); int GetH() return H; int GetW() return W;private: int H,W;void Rectangle:InitR(int x,int y,int w,int h) InitL(x,y); W=w; H=h;公有继承#include using namespace std;void main() Rectangle rect; rect.InitR(2,3,20,10); rect.Move(3,2); coutrect.GetX(), rect.GetY(), rect.GetH(), rect.GetW()endl;输出:5,5,10,20/派生类class V:public Rectanglepublic: void Function();void V:Function() Move(3,2);公有继承若继承方式为private,Move(3,2)是否正确?为什么?若若继承方式承方式为private,Move(3,2)仍然仍然正确正确。原因原因:由于:由于类Rectangle对类Location是公有是公有继承,承,而而类V对类Rectangle是是直接直接继承承,因此在,因此在类V内内可以可以访问基基类Location的公有成的公有成员;class Rectangle:private Locationpublic: void InitR(int x,int y,int w,int h); int GetH() return H; int GetW() return W;private: int W,H;void Rectangle:InitR(int x,int y,int w,int h) InitL(x,y); W=w; H=h;私有继承直接继承,正确#include void main() Rectangle rect; rect.InitR(2,3,20,10); rect.Move(3,2); coutrect.GetX(), rect.GetY(), rect.GetH(), rect.GetW()endl;错误/修改class Rectangle:private Locationpublic: void InitR(int x,int y,int w,int h); void Move(int xOff,int yOff) Location:Move(xOff,yOff); int GetX() return Location:GetX(); int GetY() return Location:GetY(); int GetH() return H; int GetW() return W;private: int W,H;void Rectangle:InitR(int x,int y,int w,int h) InitL(x,y); W=w; H=h;通过成员名限定符(:)指明调用基类中的成员class V:public Rectanglepublic: void Function();void V:Function() Move(3,2); 公有继承若继承方式为private,Move(3,2)是否正确?为什么?若若继承方式承方式为private,Move(3,2)仍然仍然错误。原因原因:由于:由于类Rectangle对类Location是私有是私有继承,承,而而类V对类Rectangle是是直接直接继承承,因此在,因此在类V内内不不可以可以访问基基类Location的公有成的公有成员;错误调用 成员访问权限的控制例分析下列程序中的访问权限,并回答问题。#include class Apublic: void f1();protected: int j1;private: int i1;class B:public Apublic: void f2();protected: int j2;private: int i2;class C:public Bpublic: void f3();回答下列问题,并说明原因。1、派生类、派生类B中成员函数中成员函数f2()能否访问基类能否访问基类A中的成中的成员:员:f1()、j1和和i1?2、派生类、派生类B的对象的对象b1能否访问基类能否访问基类A中的成员:中的成员:f1()、j1和和i1?可以可以访问f1()和和j1,不可以不可以访问i1;可以可以访问f1(),不可以不可以访问j1和和i1;3、派生类、派生类C中的成员函数中的成员函数f3()能否访问直接基类能否访问直接基类B中的成员:中的成员:f2()、j2和和i2?能否访问间接基类能否访问间接基类A中中的成员:的成员: f1()、j1和和i1?可以可以访问直接基直接基类中的中的f2()和和j2以及以及间接基接基类中中的的f1()和和j1,不可以不可以访问i2和和i1;4、派生类、派生类C的对象的对象c1能否访问直接基类能否访问直接基类B中的成员:中的成员:f2()、j2和和i2?能否访问间接基类能否访问间接基类A中的成员:中的成员: f1()、j1和和i1?可以可以访问直接基直接基类中的中的f2()以及以及间接基接基类中的中的f1(),其他都不可以其他都不可以访问;派生类对象生成时,要调用构造函数进行初始化。编译器的调用过程是先调用基类的构造函数,对派生类中的基类数据进行初始化,然后再调用派生类自己的构造函数,对派生类的数据进行初始化工作。析构函数的调用正好相反。 4.3派生类的构造函数和析构函数派生类的构造函数和析构函数 构造构造和析构函数的访问和析构函数的访问基类子对象基类子对象:派生类的对象中由基类中说明的:派生类的对象中由基类中说明的数据成员和操作所构成的封装体;数据成员和操作所构成的封装体;基类子对象基类子对象由由基类基类中的中的构造函数构造函数进行进行初始化初始化;构造函数不能被继承;构造函数不能被继承;派生类构造函数的工作:派生类构造函数的工作:k对自己的数据成员进行初始化;对自己的数据成员进行初始化;k负责调用基类构造函数使基类的数据成员得负责调用基类构造函数使基类的数据成员得以初始化;以初始化;k调用子对象的构造函数,对派生类中的子对调用子对象的构造函数,对派生类中的子对象进行初始化;象进行初始化; 一、派生类的构造函数一、派生类的构造函数派生类构造函数格式:派生类构造函数格式:若某项的若某项的参数表为空参数表为空,则该项可从成员初始化列,则该项可从成员初始化列表中省略,表示使用表中省略,表示使用缺省构造函数缺省构造函数初始化该基类子初始化该基类子对象;对象;():(), ()说明:#include class Apublic: A(int h,int w):he(h),we(w) A() couthe,weendl;private: int he, we;class B:public Apublic: B(int h,int w,int h1,int w1,int l):A(h,w), obj1(h1,w1)len=l; B()coutlenendl; private: A obj1; int len; ;void main() B obj(1,2,3,4,5); 派生类构造函数调用顺序派生类构造函数调用顺序:k基类的构造函数;基类的构造函数;k子对象的构造函数;子对象的构造函数;k派生类构造函数体;派生类构造函数体; 二、派生类的析构函数执行派生类的析构函数时,基类的析构函数也执行派生类的析构函数时,基类的析构函数也将被调用;将被调用;析构函数不能被继承;析构函数不能被继承;析构函数的执行顺序与构造函数严格析构函数的执行顺序与构造函数严格相反相反;k派生类的析构函数;派生类的析构函数;k基类的析构函数;基类的析构函数;例分析下列程序的输出结果。#include class Bpublic: B(); B(int i); B(); void Print() const;private: int b;B:B() b=0; coutBs default constructor called. endl;B:B(int i) b=i; coutBs constructor called. endl; B:B() coutBs destructor called. endl;void B:Print() const coutbendl;class C:public Bpublic: C(); C(int i,int j); C(); void Print() const; private: int c;C:C() c=0; coutCs default constructor called. endl;C:C(int i,int j):B(i) c=j; coutCs constructor called. endl;C:C() coutCs destructor called. endl;void C:Print() const B:Print(); coutcendl;void main() C obj(5,6); obj.Print();输出:Bs constructor called.Cs constructor called.56Cs destructor called.Bs destructor called.三、派生类构造函数使用中应注意的问题派生类派生类构造函数的定义中可以构造函数的定义中可以省略省略对对基类构造基类构造函数函数的调用,其条件是在基类中必须有的调用,其条件是在基类中必须有缺省的缺省的构造函数构造函数或者根本或者根本没有定义没有定义任何任何构造函数构造函数;当基类的构造函数使用一个或多个参数时,派当基类的构造函数使用一个或多个参数时,派生类必须定义构造函数,提供将参数传递给基生类必须定义构造函数,提供将参数传递给基类构造函数的途径;类构造函数的途径;编译器自动生成缺省构造函数设基类数据成员为m个,派生类数据成员为n个,派生类的参数个数为x,则:0=x=m+n;4.4 多继承的概念class : , ;一、多继承派生类的声明形式:二、多继承的构造函数():(), () (),.多继承构造函数格式:多继承构造函数格式:二、多继承的构造函数(续)派生类构造函数负责所有基类构造函数的调用;派生类构造函数负责所有基类构造函数的调用;派生类构造函数执行顺序:派生类构造函数执行顺序:k执行所有基类的构造函数;执行所有基类的构造函数;k执行所有子对象的构造函数;执行所有子对象的构造函数;k执行派生类构造函数体;执行派生类构造函数体;处于同一层次的处于同一层次的各基类构造函数的执行顺序各基类构造函数的执行顺序取决取决于于定义派生类定义派生类时所指定的各基类时所指定的各基类顺序顺序,与派生类,与派生类构造函数中所定义的成员初始化列表中的各项顺构造函数中所定义的成员初始化列表中的各项顺序无关;序无关;例分析下列程序的输出结果。#include class B1public: B1(int i) b1=i; coutConstructor B1. endl; void Print() coutb1endl;private: int b1;class B2public: B2(int i) b2=i; coutConstructor B2. endl; void Print() coutb2endl;private: int b2;class B3public: B3(int i) b3=i; coutConstructor B3. endl; int Getb3() return b3;private: int b3;class A:public B2,public B1public: A(int i,int j,int k,int l);多继承 void Print();private: int a; B3 bb;A:A(int i,int j,int k,int l):B1(i),B2(j),bb(k) a=l; coutConstructor A. endl; void A:Print() B1:Print(); B2:Print(); coutabb.Getb3()endl;子对象基类构造函数调用顺序与定义时的顺序不同void main() A aa(1,2,3,4); aa.Print();输出Constructor B2.Constructor B1.Constructor B3.Constructor A.124 3三、二义性问题1、产生二义性的原因在在多继承多继承情况下,造成的对情况下,造成的对基类基类中中某个成员某个成员的的访问访问出现的出现的不唯一不唯一的情况;的情况;class Aclass C:public A,public B public: public: void f(); void g(); void h();class B ;public: void f(); void g();void f();c1.f()A.f()B.f()C三、二义性问题(续)问题:若定义C c1;,则c1.f()是否正确?答答:c1.f()将将产生生二二义性性;原因原因:不能不能识别是是调用用类A或或类B的的f函数;函数;解决方法:解决方法:a.区区别出是出是类A或或类B的的f函数;函数;c1.A:f();或或c1.B:f();b.在在类中定中定义同名函数同名函数f;当一个当一个派生类派生类从从多个基类多个基类派生,而这些基类又派生,而这些基类又有一个有一个共同的基类共同的基类,则对该基类中说明的成员,则对该基类中说明的成员进行访问时,可能会出现二义性;进行访问时,可能会出现二义性;三、二义性问题(续)class Aclass B2:public A public: private: int a; int b2; ;class B1:public Aclass C:public B1,public B2 private: public: int b1; int f(); private: int c; ;c1.aA.aB1.b1CB2.b2A.a三、二义性问题(续)问题:若定义C c1;,则c1.a与c1.A:a是否正确?答答:c1.a与与c1.A:a将将产生生二二义性性;原因原因:不能不能识别是通是通过类B1或或类B2调用用类A的的a;解决方法:解决方法:a.区区别出是通出是通过类B1或或类B2调用用类A的的a;c1.B1:a;或或c1.B2:a;b.在在类中定中定义一个同名成一个同名成员;2、解决方法、解决方法利用成员名限定法消除二义性;利用成员名限定法消除二义性;在类中定义一个同名成员;在类中定义一个同名成员;虚基类;虚基类;例分析下列程序的输出结果。 #include class A public: A(int i) a=i; coutConstructor A. iendl; A() coutDestructor A. endl; void Print() coutaendl; private: int a; ; class B1:public A public: B1(int i,int j):A(i) b1=j; coutConstructor B1. endl; B1() coutDestructor B1. endl; void Print() A:Print(); coutb1endl; private: int b1;class B2:public Apublic: B2(int i,int j):A(i) b2=j; coutConstructor B2. endl; B2() coutDestructor B2. endl; void Print() A:Print();coutb2endl; private: int b2;class C:public B1,public B2public: C(int i,int j,int k,int l,int m):B1(i,j),B2(k,l),c(m) coutConstructor C. endl; C() coutDestructor C. endl; void Print() B1:Print(); B2:Print(); coutcendl; private: int c;void main() C c1(1,2,3,4,5); c1.Print();Constructor A.1Constructor B1. Constructor A.3Constructor B2.Constructor C.12345Destructor C.Destructor B2.Destructor A.Destructor B1.Destructor A.执行结果4.5虚基类的引入和说明引入目的:解决二义性问题,在继承间接共同基类时只保留一份成员格式:格式:virtual 说明:说明:关键字关键字virtual与关键字与关键字public或或private的相对的相对位置无关,但必须位于虚基类名之前,且位置无关,但必须位于虚基类名之前,且virtual只只对紧随其后的基类名起作用;对紧随其后的基类名起作用;例如:例如:class D:virtual public A,private B,virutal public C其中:其中:类A和和类C是是虚基虚基类,而,而类B是是非虚基非虚基类;class Apublic: void f();protected: int a;class B:virtual public Aprotected: int b;class C:virtual public Bprotected: int c;虚基类虚基类;class D:public B,public Cpublic: int g();private: int d;下列各下列各语句是否正确?句是否正确?D n;n.f();void D:g() f();BD.g()CA.f()n.f()正确能够唯一确定调用类A的f();虚基类非虚基类BCADBCDAA虚基类与非虚基类的存储结构一、虚基类的构造函数派生类中如果只有派生类中如果只有一个虚基类子对象一个虚基类子对象; 虚基类构造函数必须只被调用一次,目的是要虚基类构造函数必须只被调用一次,目的是要保证虚基类子对象只被初始化一次;保证虚基类子对象只被初始化一次;虚基类子对象虚基类子对象由由最后派生类最后派生类的构造函数通过调用的构造函数通过调用虚基类的构造函数进行虚基类的构造函数进行初始化初始化;在一个成员初始化列表中出现对虚基类和对非虚在一个成员初始化列表中出现对虚基类和对非虚基类构造函数的调用时,基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数先于非虚基类的构造函数的执行虚基类的构造函数的执行;最后派生类的构造函数的成员初始化列表中必须最后派生类的构造函数的成员初始化列表中必须给出对虚基类的构造函数的调用;如果未列出,则给出对虚基类的构造函数的调用;如果未列出,则相应的虚基类必须有缺省构造函数;相应的虚基类必须有缺省构造函数;基类子对象数据成员初始化虚基类非虚基类常数据成员静态数据成员一般数据成员存在多个基类时,执行顺序取决于派生类定义时的顺序存在多个子对象时,执行顺序取决于类中声明的顺序成员初始化列表类外初始化基类与派生类的转换子类型:共用派生类具体转换:派生类对象可以向基类对象赋值;派生类对象可以替代基类对象向基类对象进行赋值或初始化;如果函数参数是基类对象或基类对象的引用,相应的实参可以用子类对象;派生类对象的地址可以赋给指向基类对象的指针变量。实验三作业:(P166) 一,二,三做到书上验证例题调试(P176)3(1),(2)(3)已知圆类,派生出圆柱类,求圆柱体积,表面积。调试(2)(3)完成实验报告C+语言程序设计第5章 多态性与虚函数本章主要内容重载静态关联与动态关联虚函数纯虚函数与抽象类虚析构函数 5.1重载在类中,构造函数可以重载,普通成员函数也可以重载;构造函数重载给初始化带来了多种方式,为用户提供了更大的灵活性。一、函数重载一、函数重载二、运算符重载的几个问题1、哪些运算符可以重载?算术运算符:算术运算符:+、-、*、/、%、+、-;位操作运算符:位操作运算符:&、|、;逻辑运算符:逻辑运算符:!、&、|;比较运算符:比较运算符:、=、=、=、!=;赋值运算符:赋值运算符:=、+=、-=、*=、/=、%=、 &=、|=、=、=;其他运算符:其他运算符:、()、-、 、new、delete、 new、delete、-*;不允许重载不允许重载的运算符:的运算符:.、*、:、?:、sizeof;2、编译程序如何选用哪一个运算符函数?运算符实质上是函数,遵循函数重载原则;运算符实质上是函数,遵循函数重载原则;3、运算符重载时必须遵循哪些原则?、运算符重载时必须遵循哪些原则?重载运算符含义必须清楚;重载运算符含义必须清楚;重载运算符不能有二义性;重载运算符不能有二义性;4、重载运算符有哪些限制?、重载运算符有哪些限制?不可臆造新的运算符;不可臆造新的运算符;重载运算符坚持重载运算符坚持4个个“不能改变不能改变”:k不能改变运算符操作数的个数;不能改变运算符操作数的个数;k不能改变运算符原有的优先级;不能改变运算符原有的优先级;k不能改变运算符原有的结合性;不能改变运算符原有的结合性;k不能改变运算符原有的语法结构;不能改变运算符原有的语法结构;函数类型 operator 运算符名称(形参表列) 对运算符的重载处理其参数中至少有一个为类对象或类对象的引用。 三、运算符重载函数一般格式三、运算符重载函数一般格式四、运算符重载函数的两种形式1、重载为类的成员函数重载重载一元一元运算符,运算符,不不再再显式说明参数显式说明参数;重载重载二元二元运算符,只运算符,只显式说明一个参数显式说明一个参数;该参数为操作数的该参数为操作数的右操作数右操作数,左操作数左操作数由由this指针指针(指向调用该成员函数的对象)(指向调用该成员函数的对象)提供;提供;重载为成员函数时,隐含了一个参数重载为成员函数时,隐含了一个参数(this指针指针);例分析下列程序的输出结果。#include class complexpublic: complex(double r=0,double i=0); complex operator +(const complex& c); complex operator -(const complex& c); complex operator -(); void print() const;private: double real,imag;complex:complex(double r,double i)+运算符运算符-运算符运算符求求负运算符运算符 real=r; imag=i;complex complex:operator +(const complex& c) double r=real+c.real; double i=imag+c.imag; return complex(r,i);complex complex:operator -(const complex& c) double r=real-c.real; double i=imag-c.imag; return complex(r,i);complex complex:operator -() return complex(-real,-imag);void complex:print() const cout (real, imag) endl;void main() complex c1(2.5,3.7),c2(4.2,6.5); complex c; c=c1-c2; c.print(); c=c1+c2;c=c1.operator -(c2);c=c1.operator +(c2); c.print(); c=-c1; c.print();输出(-1.7,-2.8)(6.7,10.2)(-2.5,-3.7)c=c1.operator -();2、重载为友元函数重载为重载为友元函数友元函数时,没有隐含的参数时,没有隐含的参数this指针指针,即即不改变不改变原有运算符的原有运算符的语法结构语法结构;重载为友元函数的运算符重载函数的格式:重载为友元函数的运算符重载函数的格式:friend operator ()例分析下列程序的输出结果。#include class complexpublic: complex(double r=0,double i=0); friend complex operator + (const complex& c1,const complex& c2); friend complex operator - (const complex& c1,const complex& c2); friend complex operator -(const complex& c); void print() const;private: double real,imag;+运算符运算符-运算符运算符求求负运算符运算符complex:complex(double r,double i) real=r; imag=i;complex operator +(const complex& c1, const complex& c2) double r=c1.real+c2.real; double i=c1.imag+c2.imag; return complex(r,i);complex operator -(const complex& c1, const complex& c2) double r=c1.real-c2.real; double i=c1.imag-c2.imag; return complex(r,i);complex operator -(const complex& c) return complex(-c.real,-c.imag);void complex:print() const cout (real, imag) endl;void main() complex c1(2.5,3.7),c2(4.2,6.5); complex c; c=c1-c2; c.print(); c=c1+c2; c.print(); c=-c1; c.print();输出(-1.7,-2.8)(6.7,10.2)(-2.5,-3.7)c=operator -(c1,c2);c=operator +(c1,c2);c=operator -(c1);3、两种重载形式的比较一般情况下,一般情况下,单目单目运算符最好重载为运算符最好重载为成员成员函数函数;双目双目运算符则最好重载为运算符则最好重载为友元函数友元函数;如果重载为成员函数,必须要求运算表达如果重载为成员函数,必须要求运算表达式第一个参数是类对象,且与运算符函数式第一个参数是类对象,且与运算符函数类型相同。类型相同。如果重载为友员函数,必须有两个形参,如果重载为友员函数,必须有两个形参,不能省略。不能省略。多态性多态性:发出:发出同样的消息同样的消息被被不同类型的不同类型的对象对象接受导致完全接受导致完全不同的行为不同的行为;多态可分为:静态多态性与动态多态性;多态可分为:静态多态性与动态多态性;动态多态性必须存在于动态多态性必须存在于继承继承的环境之中;的环境之中;多态性的概念概念概念:静态关联静态关联:在:在编译编译时进行的关联,即编译时就时进行的关联,即编译时就确定了程序中的操作调用与执行该操作代码之确定了程序中的操作调用与执行该操作代码之间的关系。间的关系。关联:关联:确定调用具体对象的过程。确定调用具体对象的过程。(把一个标识符和一个地址联系起来把一个标识符和一个地址联系起来)5.2静态关联和动态关联静态关联和动态关联动态关联:动态关联:在在程序执行程序执行时进行的关联;时进行的关联;实现实现:C+动态关联在动态关联在虚函数虚函数的支持下实现的支持下实现;5.3虚函数1、虚函数、虚函数虚函数是动态关联的基础;虚函数是动态关联的基础;virtual ()说明方法:说明方法:含义:含义:若若类中一成中一成员函数被函数被说明明为虚函数虚函数,则该成成员函数在派生函数在派生类中可能有不同的中可能有不同的实现。当使用。当使用该成成员函数操作函数操作指指针或引用或引用所所标识的的对象象时,对该成成员函数函数调用可采用用可采用动态关关联方式。方式。例分析下列程序的输出结果。#include class Pointpublic: Point(double i,double j) x=i;y=j; virtual double Area() const return 0;private: double x,y;class Rectangle:public Pointpublic: Rectangle(int i,int j,int k,int l); virtual double Area() const return w*h;private:虚函数虚函数虚函数虚函数 double w,h;Rectangle:Rectangle(int i,int j,int k,int l):Point(i,j) w=k; h=l;void fun(Point &s) couts.Area()endl;void main() Rectangle rect(3.0,5.2,15.0,25.0); fun(rect);输出:375例分析下列程序的输出结果,并回答问题。#include class Apublic: virtual void act1() coutA:act1() called. endl; void act2() act1(); ;class B:public Apublic: void act1() coutB:act1() called. act1();输出结果是什么?为什么?输出结果是什么?为什么?输出出结果果与与(1)相同,即:相同,即:B:act1() called.原因原因:this指向操作指向操作该成成员函数的函数的对象,基于与象,基于与(1)相同的原因,此相同的原因,此处调用用B:act1()。(3)、如果将、如果将A:act2()的实现改为:的实现改为:void A:act2()A:act1();输出结果是什么?为什么?输出结果是什么?为什么?输出出结果果:A:act1() called.原因原因:此:此处增加了成增加了成员名限定,因此要名限定,因此要进行静行静态关关联,即,即调用的是用的是A:act1()。派生类中对基类的虚函数进行替换时,要求派派生类中对基类的虚函数进行替换时,要求派生类中说明的虚函数与基类中的被替换的虚函数之生类中说明的虚函数与基类中的被替换的虚函数之间满足下列条件:间满足下列条件:参数个数参数个数:与基类的虚函数有:与基类的虚函数有相同相同的参数个数;的参数个数;参数类型参数类型:与基类的虚函数的对应参数类型:与基类的虚函数的对应参数类型相同相同;返回值类型:返回值类型: 与基类的虚函数的返回值类型与基类的虚函数的返回值类型相同相同;满足上述条件的派生足上述条件的派生类虚函数,可不加虚函数,可不加virtual说明。明。5.4 纯虚函数1、引入、引入在基类中不能为虚函数给出一个有意义的实现在基类中不能为虚函数给出一个有意义的实现时,可将其声明为纯虚函数,其实现留待派生类完时,可将其声明为纯虚函数,其实现留待派生类完成;成;2、作用、作用为派生类提供一个一致的接口;为派生类提供一个一致的接口;3、声明格式、声明格式class virtual ()=0;例分析下列程序的输出结果。#include class Pointpublic: Point(int i=0,int j=0) x0=i;y0=j; virtual void Set()=0; virtual void Draw()=0;protected: int x0,y0;class Line:public Pointpublic: Line(int i=0,int j=0,int m=0,int n=0):Point(i,j) x1=m;y1=n; void Set() coutLine:Set() called. endl; void Draw() coutLine:Draw() called. endl;protected: int x1,y1;class Ellipse:public Pointpublic: Ellipse(int i=0,int j=0,int p=0,int q=0):Point(i,j) x2=p;y2=q; void Set() coutEllipse:Set() called. endl; void Draw() coutEllipse:Draw() called. Draw();void SetObj(Point* p) p-Set();void main() Line *lineobj=new Line; Ellipse *ellipseobj=new Ellipse; DrawObj(lineobj); DrawObj(ellipseobj); coutendl; SetObj(lineobj); SetObj(ellipseobj); coutendlRedraw the objectendl; DrawObj(lineobj); DrawObj(ellipseobj);Line:Draw() called.Ellipse:Draw() called.Line:Set() called.Ellipse:Set() called.Redraw the object.Line:Draw() called.Ellipse:Draw() called.执行结果5.5 抽象类带有带有纯虚函数纯虚函数的类称为的类称为抽象类抽象类;抽象类只能抽象类只能作为基类作为基类使用,其纯虚函数的实现由使用,其纯虚函数的实现由派生类给出;但派生类仍可不给出纯虚函数的定派生类给出;但派生类仍可不给出纯虚函数的定义,继续作为抽象类存在;义,继续作为抽象类存在;抽象类抽象类不能定义对象不能定义对象,一般将该类的,一般将该类的构造函数构造函数说说明为明为保护保护的访问控制的访问控制权限权限;抽象类的抽象类的作用作用:k用作基类用作基类:在一个继承层次结构中,提供一个:在一个继承层次结构中,提供一个公共的根,并基于抽象类的操作设计出对抽象公共的根,并基于抽象类的操作设计出对抽象类所描述的一类对象进行操作的公共接口,其类所描述的一类对象进行操作的公共接口,其完整的实现由派生类完成;完整的实现由派生类完成;k用作指针或引用的基类型用作指针或引用的基类型:保证进入继承层次:保证进入继承层次的每个类都具有(提供)纯虚函数所要求的行的每个类都具有(提供)纯虚函数所要求的行为;为;在在成员函数成员函数内内可以可以调用调用纯虚函数纯虚函数,但在,但在构造函数构造函数或或析构函数析构函数内内不能调用不能调用纯虚函数(纯虚函数没有纯虚函数(纯虚函数没有实现代码);实现代码);class Apublic:virtual void f()=0;void g() f(); A() f(); 正确正确错误5.6虚析构函数虚析构函数虚析构函数在在析构函数前析构函数前加关键字加关键字virtual进行说明,则该进行说明,则该析构函数称为虚析构函数;析构函数称为虚析构函数;格式:格式:class Bpublic:virtual B();.如果一个类的析构函数被说明为虚析构函数,则如果一个类的析构函数被说明为虚析构函数,则它的派生类中的析构函数也是虚析构函数,不管它的派生类中的析构函数也是虚析构函数,不管它是否使用了关键字它是否使用了关键字virtual进行说明;进行说明;目的目的:使用使用delete运算符删除一个对象时,能保证析运算符删除一个对象时,能保证析构函数被正确地执行;构函数被正确地执行;例分析下列程序的输出结果。#include class Apublic: virtual A() coutA:A() called. endl;class B:public Apublic: B(int i) buf=new chari; virtual B() delete buf; coutB:B() called. endl; private: char *buf;void fun(A *a) delete a;void main() A *a=new B(15); fun(a);输出:出: B:B() called. A:A() called.进行行动态关关联,调用用B的析的析构函数,而构函数,而B是是A的派生的派生类,进一步一步调用用A的析构函数;的析构函数;问:若类问:若类A的析构函数不是虚析构函数,则输出结的析构函数不是虚析构函数,则输出结果是什么?为什么?会导致什么后果?果是什么?为什么?会导致什么后果?输出出结果果为:A:A() called.原因原因:此:此时进行静行静态关关联,直接,直接调用基用基类A的析的析构函数;构函数;后果后果:此:此时将不能将不能释放放类B中的中的资源;源;说明:说明:子类型化要求析构函数被声明为虚函数,尤其是子类型化要求析构函数被声明为虚函数,尤其是在析构函数要完成一些有意义的工作时;在析构函数要完成一些有意义的工作时;构造函数不能被声明为虚函数;构造函数不能被声明为虚函数;实验四P180一,二,三做到书上P187,3(2)面向对象大作业定义抽象基类shape类,point类,circle类,cylinder类都是shape类的直接派生类,间接派生类。求面积,体积,实现动态关联。
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号