版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第十二章第十二章 类的其它特类的其它特性性 友元函数友元函数类中私有和保护的成员在类外不能被访问。类中私有和保护的成员在类外不能被访问。友元函数是一种定义在类外部的友元函数是一种定义在类外部的普通函普通函数数,其特点是,其特点是能够访问类中私有成员和能够访问类中私有成员和保护成员保护成员,即类的访问权限的限制对其,即类的访问权限的限制对其不起作用。不起作用。友元函数需要在友元函数需要在类体内类体内进行说明,在前面加进行说明,在前面加上关键字上关键字friend。一般格式为:一般格式为:friend FuncName();friend float Volume(A &a);关键字关键字返
2、回值类型返回值类型函数名函数名函数参数函数参数友元函数不是成员函数友元函数不是成员函数,用法也与普通,用法也与普通的函数完全一致,的函数完全一致,只不过它能访问类中只不过它能访问类中所有的数据所有的数据。友元函数破坏了类的封装友元函数破坏了类的封装性和隐蔽性,使得非成员函数可以访问性和隐蔽性,使得非成员函数可以访问类的私有成员类的私有成员。一个类的友元可以自由地用该类中的所有成员。一个类的友元可以自由地用该类中的所有成员。class Afloat x,y;public: A(float a, float b) x=a; y=b; float Sum() return x+y; friend f
3、loat Sum(A &a) return a.x+a.y; ;void main(void) A t1(4,5),t2(10,20); coutt1.Sum()endl; coutSum(t2)endl;友元函数友元函数成员函数成员函数友元函数的调用,直接调用友元函数的调用,直接调用成员函数的调用,利用对象名调用成员函数的调用,利用对象名调用友元函数只能用友元函数只能用对象对象名名引用类中的数据。引用类中的数据。私有数据私有数据有关友元函数的使用,说明如下:有关友元函数的使用,说明如下:友元函数不是类的成员函数友元函数不是类的成员函数友元函数近似于友元函数近似于普通的函数普通的函数,
4、它不带有,它不带有this指针,指针,因此必须将对象名或对象的引用作为因此必须将对象名或对象的引用作为友元函数的参数友元函数的参数,这样才能访问到对象的成,这样才能访问到对象的成员。员。友元函数与一般函数的不同点在于友元函数与一般函数的不同点在于:1.友元函数必须在类的定义中说明,友元函数必须在类的定义中说明,其其函数体可在类内定义,也可在类外定函数体可在类内定义,也可在类外定义义;2.它可以访问该类中的所有成员(公有它可以访问该类中的所有成员(公有的、私有的和保护的)的、私有的和保护的),而一般函数,而一般函数只能访问类中的公有成员。只能访问类中的公有成员。class Afloat x,y;
5、public: A(float a, float b) x=a; y=b; float Getx() return x; float Gety() return y; float Sum() return x+y; friend float Sum(A &); ;float Sumxy(A &a) return a.Getx()+a.Gety(); float Sum(A &a) return a.x+a.y; void main(void) A t1(1,2),t2(10,20), t3(100,200); coutt1.Sum()endl; coutSum(t2)e
6、ndl; coutSumxy(t3)endl;成员函数成员函数友元函数友元函数,可以直接调用类中私有成员可以直接调用类中私有成员普通函数,必须通过公有函数访问私有成员普通函数,必须通过公有函数访问私有成员对象调用成员函数对象调用成员函数调用友元函数调用友元函数调用一般函数调用一般函数友元函数友元函数友元函数不受类中访问权限关键字的限制,可以友元函数不受类中访问权限关键字的限制,可以把它放在类的私有部分,放在类的公有部分或放把它放在类的私有部分,放在类的公有部分或放在类的保护部分,其作用都是一样的。换言之,在类的保护部分,其作用都是一样的。换言之,在类中对友元函数指定访问权限是不起作用的在类中对
7、友元函数指定访问权限是不起作用的。友元函数的作用域与一般函数的作用域相同。友元函数的作用域与一般函数的作用域相同。谨慎使用友元函数谨慎使用友元函数通常使用友元函数来通常使用友元函数来取取对象中的数据成员值,而对象中的数据成员值,而不修改不修改对象中的成员值,则肯定是安全的。对象中的成员值,则肯定是安全的。大多数情况是友元函数是某个类的成员函数,大多数情况是友元函数是某个类的成员函数,即即A类中的某个成员函数是类中的某个成员函数是B类中的友元函数,这个成类中的友元函数,这个成员函数可以直接访问员函数可以直接访问B类中的私有数据。类中的私有数据。这就实现这就实现了类与类之间的沟通了类与类之间的沟通
8、。注意:一个类的成员函数作为另一个类的友元函数时,注意:一个类的成员函数作为另一个类的友元函数时,应应先定义友元函数所在的类。先定义友元函数所在的类。class A.void fun( B &);class B.friend void fun( B &);既是类既是类A的成员函数的成员函数又是类又是类B的友元函数的友元函数class B ;/先定义类先定义类A,则首先对类,则首先对类B作引用性说明作引用性说明class A ./类类A的成员定义的成员定义 public: void fun( B & );/函数的原型说明函数的原型说明 ;class B. friend v
9、oid A:fun( B & );/定义友元函数定义友元函数; void A:fun ( B &b) /函数的完整定义函数的完整定义 ./函数体的定义函数体的定义类类A中的成员函数中的成员函数fun()是类是类B的友元的友元函数。即在函数。即在fun()中中可以直接引用类可以直接引用类B的的私有成员。私有成员。class B;/必须在此进行引用性说明,必须在此进行引用性说明,class Afloat x,y;public: A(float a, float b) x=a; y=b; float Sum(B &); /说明友元函数的函数原型,是类说明友元函数的函数原型,是
10、类A的一成员函数的一成员函数;class Bfloat m,n;public: B(float a,float b) m=a;n=b; friend float A:Sum(B &);/说明类说明类A的成员函数是类的成员函数是类B的友元函数的友元函数float A:Sum( B &b)/定义该友元函数定义该友元函数 x=b.m+b.n; y=b.m-b.n; void main(void) A a1(3,5); B b1(10,20); a1.Sum(b1);/调用该函数,调用该函数,因是类因是类A的成员函数,故用类的成员函数,故用类A的对象调用的对象调用a1.x=30a1.y
11、=-10直接引用类直接引用类B的私有成员的私有成员类类A中有一个函数可以直接引用中有一个函数可以直接引用类类B的私有成员的私有成员友元类友元类class A . friend class B;class B . 类类B是类是类A的友元的友元类类B可以自由使用可以自由使用类类A中的成员中的成员对于类对于类B而言,类而言,类A是透明的是透明的类类B必须通过必须通过类类A的对象的对象使用类使用类A的成员的成员const float PI =3.1415926;class Afloat r ;float h;public: A(float a,float b)r=a; h=b;float Getr()
12、return r;float Geth()return h;friend class B;/定义类定义类B为类为类A的友元的友元;class B int number;public: B(int n=1)number=n;void Show(A &a) coutPI*a.r*a.r*a.h*numberendl; /求类求类A的某个对象的某个对象*n的体积的体积;void main(void)A a1(25,40),a2(10,40);B b1(2);b1.Show (a1);b1.Show (a2);直接引用类直接引用类A的私有成员的私有成员类类B中的任何函数都能使用中的任何函数都能
13、使用类类A中的所有私有成员。中的所有私有成员。不管是按哪一种方式派生,基类的私有成员不管是按哪一种方式派生,基类的私有成员在派生类中都是不可见的。在派生类中都是不可见的。如果在一个派生类中要访问基类中的私有成如果在一个派生类中要访问基类中的私有成员,可以将这个员,可以将这个派生类声明为基类的友元派生类声明为基类的友元。class Base friend class Derive; .class Derive .直接使用直接使用Base中的私有成员中的私有成员#includeclass M friend class N; /N为为M的友元,可以直接使用的友元,可以直接使用M中的私有成员中的私有成
14、员private: int i , j; void show(void)couti=itj=jt;public: M(int a=0,int b=0) i=a; j=b;class N :public M /N为为M的派生类的派生类public: N(int a=0,int b=0):M(a,b) void Print(void) show(); couti+j=i+jendl;void main(void) N n1(10,20); M m1(100,200);/ m1.show();/私有成员函数,在类外不可调用私有成员函数,在类外不可调用 n1.Print();直接引用类直接引用类M的私
15、有成员函数和私有成员的私有成员函数和私有成员基类对象基类对象 M派生类对象派生类对象 Nx(私有)私有)Show( )(私私有有)x(私私有)私私有)Show( )(私私有私私有)y(公有公有)Showy( )(公有公有)showy( ) show(); coutiShow();basep-Show()基类指针基类指针派生类对象派生类对象基类对象基类对象class Pointfloat x,y;public: Point()Point(float i,float j)x=i;y=j;float area(void)return 0.0;const float Pi=3.14159;class
16、Circle:public Point/类类Point的派生类的派生类float radius;public: Circle(float r) radius=r;float area(void) return Pi*radius*radius;void main(void) Point *pp; /基类指针,可以将派生类对象的地址赋给基类指针基类指针,可以将派生类对象的地址赋给基类指针 Circle c(5.4321); pp=&c; coutarea ()Show()Base *basep;basep=&b;basep = &d;basep -Show();即指向派生
17、类新增的成员函数即指向派生类新增的成员函数需要将基类中的需要将基类中的Show()说明为虚函数说明为虚函数若要访问派生类中相同名字的函数,必须将若要访问派生类中相同名字的函数,必须将基类中的基类中的同名函数定义为虚函数同名函数定义为虚函数,这样,将,这样,将不同的派生类对象的地址赋给基类的指针变不同的派生类对象的地址赋给基类的指针变量后,就可以量后,就可以动态地根据这种赋值语句调用动态地根据这种赋值语句调用不同类中的函数不同类中的函数。class Point float x,y;public: Point()Point(float i,float j)x=i;y=j;virtual float
18、 area(void) return 0.0; ;const float Pi=3.14159;class Circle:public Point/类类Point的派生类的派生类float radius;public: Circle(float r) radius=r;float area(void) return Pi*radius*radius;void main(void) Point *pp; /基类指针,可以将派生类对象的地址赋给基类指针基类指针,可以将派生类对象的地址赋给基类指针 Circle c(5.4321); pp=&c; coutarea ()endl; /调用虚函
19、数调用虚函数将将area()声明为虚函数,编译器对其进行动态聚束,按照实际对象声明为虚函数,编译器对其进行动态聚束,按照实际对象c调用了调用了Circle中的函数中的函数area()。使。使Point类中的类中的area()与与Circle类中的类中的area()有一个有一个统一的接口。统一的接口。输出:输出:92.7011声明为虚函数声明为虚函数调用虚函数调用虚函数虚函数再定义虚函数再定义虚函数的定义和使用虚函数的定义和使用 可以在程序运行时通过调用相同的函数名而实可以在程序运行时通过调用相同的函数名而实现不同功能的函数称为虚函数。现不同功能的函数称为虚函数。定义格式为:定义格式为:virt
20、ual FuncName();一旦把基类的成员函数定义为虚函数,由基类所一旦把基类的成员函数定义为虚函数,由基类所派生出来的所有派生类中,该函数均保持虚函数派生出来的所有派生类中,该函数均保持虚函数的特性。的特性。 在派生类中重新定义基类中的虚函数时,可以不在派生类中重新定义基类中的虚函数时,可以不用关键字用关键字virtual来修饰这个成员函数来修饰这个成员函数 。虚函数是用关键字虚函数是用关键字virtual修饰的某基类中的修饰的某基类中的protected或或public成员函数。它可以在派生成员函数。它可以在派生类中重新定义,以形成不同版本。类中重新定义,以形成不同版本。只有在程只有在
21、程序的执行过程中,依据指针具体指向哪个类序的执行过程中,依据指针具体指向哪个类对象,或依据引用哪个类对象,才能确定激对象,或依据引用哪个类对象,才能确定激活哪一个版本,实现动态聚束活哪一个版本,实现动态聚束。class Aprotected:int x;public: A()x =1000; virtual void print()cout “x=”xt; /虚函数虚函数;class B:public Aint y;public: B() y=2000;void print()cout “y=”yt; /派生虚函数派生虚函数;class C:public Aint z;public: C()z
22、=3000;void print()cout “z=”zprint();/调用类调用类A的虚函数的虚函数 pa=&b; pa-print();/调用类调用类B的虚函数的虚函数 pa=&c; pa-print();/调用类调用类C的虚函数的虚函数class Base public : virtual int Set(int a, int b) . .;class Derive:public Basepublic : int Set(int x, int y) . .;class Base public : virtual int Set(int a, int b) . .;cla
23、ss Derive:public Basepublic : int Set(int x, int y=0) . .;int Set(int ,int )是虚函数是虚函数两个两个Set()函数参数不一函数参数不一致,是重载,不是虚函致,是重载,不是虚函数数关于虚函数,说明以下几点:关于虚函数,说明以下几点:1、当在基类中把成员函数定义为虚函数后,当在基类中把成员函数定义为虚函数后,在其派生类中定义的虚函数必须与基类中的在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回的类型也相同数必须一一对应,函数的返回的
24、类型也相同。若函数名相同,但参数的个数不同或者参数若函数名相同,但参数的个数不同或者参数的类型不同时,则属于函数的重载,而不是的类型不同时,则属于函数的重载,而不是虚函数。若函数名不同,显然这是不同的成虚函数。若函数名不同,显然这是不同的成员函数。员函数。2、实现这种动态的多态性时,必须使用实现这种动态的多态性时,必须使用基类类型基类类型的指针变量的指针变量,并使该指针,并使该指针指向不同的派生类对象指向不同的派生类对象,并通过调用指针所指向的虚函数才能实现动态的并通过调用指针所指向的虚函数才能实现动态的多态性。多态性。xShow()xShow()yShow()xShow()zShow()类类
25、A类类B类类CShow()定义为虚函数定义为虚函数类类B与类与类C均为类均为类A的公有派生。的公有派生。A *p; B b;C c; p=&b ; p-Show();p=&c ; p-Show();即在程序运行时,即在程序运行时,通过赋值语句实通过赋值语句实现多态性现多态性3、虚函数必须是类的一个成员函数,不能虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数。是友元函数,也不能是静态的成员函数。4、在派生类中、在派生类中没有重新定义虚函数没有重新定义虚函数时,与时,与一般的成员函数一样,当调用这种派生类对一般的成员函数一样,当调用这种派生类对象的虚函数时,象
26、的虚函数时,则调用其基类中的虚函数则调用其基类中的虚函数。5、可把析构函数定义为虚函数,但是,不可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数能将构造函数定义为虚函数。6、虚函数与一般的成员函数相比较,、虚函数与一般的成员函数相比较,调用时的执调用时的执行速度要慢一些行速度要慢一些。为了实现多态性,在每一个派。为了实现多态性,在每一个派生类中均要保存相应虚函数的入口地址表,函数生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现的。因此,除了要编写的调用机制也是间接实现的。因此,除了要编写一些通用的程序,并一定要使用虚函数才能完成一些通用的程序,并一定要使用虚函数才能完
27、成其功能要求外,通常不必使用虚函数。其功能要求外,通常不必使用虚函数。7、一个函数如果被定义成虚函数,则不管经历多、一个函数如果被定义成虚函数,则不管经历多少次派生,仍将保持其虚特性,以实现少次派生,仍将保持其虚特性,以实现“一个接一个接口,多个形态口,多个形态”。虚函数的访问虚函数的访问用基指针访问与用对象名访问用基指针访问与用对象名访问用基指针访问虚函数时,指向其实际派生类用基指针访问虚函数时,指向其实际派生类对象重新定义的函数。实现动态聚束。对象重新定义的函数。实现动态聚束。通过一个通过一个对象名对象名访问时,只能静态聚束。即访问时,只能静态聚束。即由编译器在编译的时候决定调用哪个函数。
28、由编译器在编译的时候决定调用哪个函数。class Point float x,y;public: Point()Point(float i,float j)x=i;y=j;virtual float area(void) return 0.0; /声明为虚函数声明为虚函数;const float Pi=3.14159;class Circle:public Point/类类Point的派生类的派生类float radius;public: Circle(float r) radius=r;float area(void) return Pi*radius*radius;/虚函数再定义虚函数再定
29、义;void main(void) Point *pp; /基类指针,可以将派生类对象的地址赋给基类指针基类指针,可以将派生类对象的地址赋给基类指针 Circle c(5.4321); coutc.area()endl; coutc.Point:area()endl; coutc.Circle:area ()endl;输出:输出:92.7011092.7011可见,利用对象名进行调用与一般非虚函数没有区别。可见,利用对象名进行调用与一般非虚函数没有区别。用对象名调用用对象名调用area( )class base0public: void v(void)coutbase0n;class base
30、1:public base0public: virtual void v(void) coutbase1n; ;class A1:public base1public: void v()coutA1n; ;class A2:public A1public: void v(void)coutA2n; ;class B1:private base1public: void v(void)coutB1n; ;class B2:public B1public: void v(void)coutv(); A2 a2; (pb=&a2)-v(); B1 b1; (pb=&b1)-v();
31、B2 b2; (pb=&b2)-v();base0base0私有派生,在类外私有派生,在类外不能调用基类函数不能调用基类函数class base0public: void v(void)coutbase0n;class base1:public base0public: virtual void v(void) coutbase1n; ;class A1:public base1public: void v()coutA1n; ;class A2:public A1public: void v(void)coutA2n; ;class B1:private base1public: v
32、oid v(void)coutB1n; ;class B2:public B1public: void v(void)coutv(); A2 a2; (pb=&a2)-v();A1A2纯虚函数纯虚函数在基类中不对虚函数给出有意义的实现在基类中不对虚函数给出有意义的实现,它只是它只是在派生类中有具体的意义。这时基类中的虚函数在派生类中有具体的意义。这时基类中的虚函数只是一个入口,具体的目的地由不同的派生类中只是一个入口,具体的目的地由不同的派生类中的对象决定。这个虚函数称为的对象决定。这个虚函数称为纯虚函数纯虚函数。class virtual ()=0;.;class Aprotecte
33、d:int x;public: A()x =1000; virtual void print()=0; /定义纯虚函数定义纯虚函数;class B:public A /派生类派生类private: int y;public: B() y=2000;void print()cout “y=”yn;/重新定义纯虚函数重新定义纯虚函数;class C:public A /派生类派生类int z;public: C()z=3000;void print()cout “z=”zprint();pa=&c; pa-print(); A a; pa=&a; pa-print( );y=200
34、0z=3000抽象类抽象类不能定义抽象类的对象不能定义抽象类的对象1、在定义纯虚函数时,不能定义虚函数的、在定义纯虚函数时,不能定义虚函数的实现部分。实现部分。2、把函数名赋于、把函数名赋于0,本质上是将指向函数体,本质上是将指向函数体的指针值赋为初值的指针值赋为初值0。与定义空函数不一样,。与定义空函数不一样,空函数的函数体为空,即调用该函数时,不空函数的函数体为空,即调用该函数时,不执行任何动作。执行任何动作。在没有重新定义这种纯虚函在没有重新定义这种纯虚函数之前,是不能调用这种函数的。数之前,是不能调用这种函数的。3、把至少包含一个纯虚函数的类,称为抽把至少包含一个纯虚函数的类,称为抽象
35、类。这种类只能作为派生类的基类,不能象类。这种类只能作为派生类的基类,不能用来说明这种类的对象用来说明这种类的对象。其理由是明显的:因为虚函数没有实现部分,其理由是明显的:因为虚函数没有实现部分,所以不能产生对象。但可以定义指向抽象类所以不能产生对象。但可以定义指向抽象类的指针,即指向这种基类的指针。当用这种的指针,即指向这种基类的指针。当用这种基类指针指向其派生类的对象时,基类指针指向其派生类的对象时,必须在派必须在派生类中生类中重载重载纯虚函数,否则会产生程序的运纯虚函数,否则会产生程序的运行错误。行错误。4、在以抽象类作为基类的派生类中必须有在以抽象类作为基类的派生类中必须有纯虚函数的实
36、现部分,即必须有重载纯虚函纯虚函数的实现部分,即必须有重载纯虚函数的函数体。否则,这样的派生类也是不能数的函数体。否则,这样的派生类也是不能产生对象的。产生对象的。综上所述,可把纯虚函数归结为:综上所述,可把纯虚函数归结为:抽象类的抽象类的唯一用途是为派生类提供基类,纯虚函数的唯一用途是为派生类提供基类,纯虚函数的作用是作为派生类中的成员函数的基础,并作用是作为派生类中的成员函数的基础,并实现动态多态性。实现动态多态性。虚基类虚基类多基派生中的多条路径具有公共基类时,在这条路径的汇合处就会因对公共基类产生多多基派生中的多条路径具有公共基类时,在这条路径的汇合处就会因对公共基类产生多个拷贝而产生
37、同名函数调用的二义性。个拷贝而产生同名函数调用的二义性。解决这个问题的办法就是把解决这个问题的办法就是把公共基类定义为虚基类公共基类定义为虚基类,使由它派生的多条路径的汇聚处只,使由它派生的多条路径的汇聚处只产生一个拷贝。产生一个拷贝。class Base ;class A : public Base ;class B: public Base ;class C: public A, public B ;类类C中继承了两个类中继承了两个类Base,即有两个,即有两个类类Base的实现部分,在调用时产生了的实现部分,在调用时产生了二义性。二义性。用虚基类进行多重派生时,用虚基类进行多重派生时,若
38、虚基类没有缺若虚基类没有缺省的构造函数省的构造函数,则在每一个派生类的构造函,则在每一个派生类的构造函数中数中都必须有对虚基类构造函数的调用都必须有对虚基类构造函数的调用 (且(且首先调用)。首先调用)。由虚基类派生出的对象初始化时,由虚基类派生出的对象初始化时,直接调用直接调用虚基类的构造函数。因此,若将一个类定义虚基类的构造函数。因此,若将一个类定义为虚基类,则一定有正确的构造函数可供所为虚基类,则一定有正确的构造函数可供所有派生类调用。有派生类调用。class basepublic:virtual void a()couta() in basen;virtual void b()cout
39、b() in basen;virtual void c()coutc() in basen;virtual void d()coutd() in basen;virtual void e()coute() in basen;virtual void f() coutf() in basen;class A:public basepublic:virtual void a()couta() in An;virtual void b()coutb() in An;virtual void f() coutf() in An;class B:public basepublic:virtual voi
40、d a()couta() in Bn;virtual void b()coutb() in Bn;virtual void c()coutc() in Bn;class C:public A,public Bpublic:virtual void a()couta() in Cn;virtual void d()couta(); pa-b(); pa-c(); pa-d(); pa-e(); pa-f();将类将类C的地址赋的地址赋值时产生歧义值时产生歧义a( )b( )c( )d( )e( )f( )a( )b( )c( )d( )e( )f( )a( )b( )f( )a( )b( )c(
41、 )d( )e( )f( )a( )c( )a( )b( )c( )d( )e( )f( )a( )b( )f( )a( )b( )c( )d( )e( )f( )a( )c( )baseABCa( )d( )ABclass basepublic:virtual void a()couta() in basen;virtual void b()coutb() in basen;virtual void c()coutc() in basen;virtual void d()coutd() in basen;virtual void e()coute() in basen;virtual voi
42、d f() coutf() in basen;class A:public basepublic:virtual void a()couta() in An;virtual void b()coutb() in An;virtual void f() coutf() in An;class B:public basepublic:virtual void a()couta() in Bn;virtual void b()coutb() in Bn;virtual void c()coutc() in Bn;class C:public A,public Bpublic:virtual void
43、 a()couta() in Cn;virtual void d()couta(); pa-b(); pa-c(); pa-d(); pa-e(); pa-f();将类将类C的地址赋的地址赋值时产生歧义值时产生歧义类类C中有两个中有两个base,只有一个,只有一个Aa() in Cb() in Ac() in based() in Ce() in basef() in A为避免这种情况,将为避免这种情况,将base定义定义为虚基类。为虚基类。class basepublic:virtual void a()couta() in basen;virtual void b()coutb() in
44、basen;virtual void c()coutc() in basen;virtual void d()coutd() in basen;virtual void e()coute() in basen;virtual void f() coutf() in basen;class A:virtual public basepublic:virtual void a()couta() in An;virtual void b()coutb() in An;virtual void f() coutf() in An;class B:virtual public basepublic:vi
45、rtual void a()couta() in Bn;virtual void c()coutc() in Bn;class C:public A,public Bpublic:virtual void a()couta() in Cn;virtual void d()couta(); pa-b(); pa-c(); pa-d(); pa-e(); pa-f();a( )b( )c( )d( )e( )f( )a( )b( )c( )d( )e( )f( )a( )b( )f( )a( )b( )c( )d( )e( )f( )a( )c( )a( )b( )c( )d( )e( )f( )
46、a( )b( )f( )a( )c( )baseABCa( )d( )ABclass basepublic:virtual void a()couta() in basen;virtual void b()coutb() in basen;virtual void c()coutc() in basen;virtual void d()coutd() in basen;virtual void e()coute() in basen;virtual void f() coutf() in basen;class A:virtual public basepublic:virtual void
47、a()couta() in An;virtual void b()coutb() in An;virtual void f() coutf() in An;class B:virtual public basepublic:virtual void a()couta() in Bn;virtual void c()coutc() in Bn;class C:public A,public Bpublic:virtual void a()couta() in Cn;virtual void d()couta(); pa-b(); pa-c(); pa-d(); pa-e(); pa-f();类类
48、C中只有一个中只有一个basea() in Cb() in Ac() in Bd() in Ce() in basef() in Aclass basepublic:void a()couta() in basen;void b()coutb() in basen;void c()coutc() in basen;void d()coutd() in basen;void e()coute() in basen;void f()coutf() in basen;class A:virtual public basepublic:void a()couta() in An;void b()cou
49、tb() in An;void f()coutf() in An;class B:virtual public basepublic:void a()couta() in Bn;void c()coutc() in Bn;class C:public A,public Bpublic:void a()couta() in Cn;void d()couta(); pa-b(); pa-c(); pa-d(); pa-e(); pa-f();类类C中只有一个中只有一个basea() in baseb() in basec() in based() in basee() in basef() in
50、base下面程序的输出是下面程序的输出是 。class Aprotected:int x;public:A()x =1000;virtual void p()cout x=xn; p2(); virtual void p2()coutA:p2()endl; ;class C:public Aint z;public:C()z=3000; void p()cout z=zn; p2();virtual void p2()coutC:p2()p();pa=&c;pa-p(); 通常,每当说明一个对象时,把该类中的有关成员通常,每当说明一个对象时,把该类中的有关成员数据拷贝到该对象中,即同一
51、类的不同对象,数据拷贝到该对象中,即同一类的不同对象,其成员数据其成员数据之间是互相独立的之间是互相独立的。静态成员静态成员 class A int x,y; public: void Setxy(int a, int b) x=a; y=b;A a1, a2;a1.xa1.ya2.xa2.y.x=a ;y=b ;.a1. Setxy()a2. Setxy()a1.Setxy(1,2);a2.Setxy(3,4);this-x=a;this-y=b;当我们将类的某一个当我们将类的某一个数据成员的存储类型指定为静态类型数据成员的存储类型指定为静态类型时时,则由该类所产生的所有对象,其静态成员均共
52、享,则由该类所产生的所有对象,其静态成员均共享一个一个存储空间存储空间,这个空间是在编译的时候分配的这个空间是在编译的时候分配的。换言之,在。换言之,在说明对象时,并不为静态类型的成员分配空间。说明对象时,并不为静态类型的成员分配空间。 在类定义中,用关键字在类定义中,用关键字static修饰的数据成员称为修饰的数据成员称为静态数据成员。静态数据成员。class A int x,y; static int z; public: void Setxy(int a, int b) x=a; y=b;A a1, a2;a1.xa1.ya2.xa2.yza1. za2. z不同对象,同一空间不同对象,
53、同一空间有关静态数据成员的使用,说明以下几点:有关静态数据成员的使用,说明以下几点:1、类的静态数据成员是、类的静态数据成员是静态分配存储空间静态分配存储空间的,而其它成员是动态分配存储空间的(全的,而其它成员是动态分配存储空间的(全局变量除外)。当类中没有定义静态数据成局变量除外)。当类中没有定义静态数据成员时,在程序执行期间遇到说明类的对象时,员时,在程序执行期间遇到说明类的对象时,才为对象的所有成员依次分配存储空间,这才为对象的所有成员依次分配存储空间,这种存储空间的分配是动态的;种存储空间的分配是动态的;而当类中定义而当类中定义了静态数据成员时,了静态数据成员时,在编译时,就要为类的在
54、编译时,就要为类的静态数据成员分配存储空间静态数据成员分配存储空间。2、必须在文件作用域中,对静态数据成员必须在文件作用域中,对静态数据成员作一次且只能作一次定义性说明作一次且只能作一次定义性说明。因为静态。因为静态数据成员在定义性说明时已分配了存储空间,数据成员在定义性说明时已分配了存储空间,所以通过静态数据成员名前加上所以通过静态数据成员名前加上类名和作用类名和作用域运算符域运算符,可直接引用静态数据成员。在,可直接引用静态数据成员。在C+中,静态变量缺省的初值为中,静态变量缺省的初值为0,所以静态,所以静态数据成员总有唯一的初值。当然,数据成员总有唯一的初值。当然,在对静态在对静态数据成
55、员作定义性的说明时,数据成员作定义性的说明时,也可以指定一也可以指定一个初值。个初值。class Aint i,j;static int x,y;/定义静态成员定义静态成员public: A(int a=0,int b=0,int c=0, int d=0)i=a;j=b;x=c;y=d;void Show()cout i=itj=jt; cout x=xty=yn; ;int A:x=0; /必须对静态成员作一次定义性说明必须对静态成员作一次定义性说明 int A:y=0; void main(void )A a(2,3,4,5);a.Show();A b(100,200,300,400);
56、b.Show();a.Show();a.x 和和b.x在内存中占据一个空间在内存中占据一个空间a.y 和和b.y在内存中占据一个空间在内存中占据一个空间i=2j=3x=4y=5i=100j=200 x=300y=400i=2j=3x=300y=400class Aint i,j;public: static int x;public: A(int a=0,int b=0,int c=0) i=a ; j=b ; x=c; void Show()cout i=itj=jt;cout x=xn;int A:x=500;/int A:xvoid main(void )A a(20,40,10),b(
57、30,50,100);a.Show ();b.Show ();cout “A:x=”A:xn; /可以直接用类名引用可以直接用类名引用在类外重新定义在类外重新定义3、静态数据成员具有全局变量和局部变量的一些、静态数据成员具有全局变量和局部变量的一些特性。静态数据成员与全局变量一样都是静态分特性。静态数据成员与全局变量一样都是静态分配存储空间的,配存储空间的,但全局变量在程序中的任何位置但全局变量在程序中的任何位置都可以访问它,而静态数据成员受到访问权限的都可以访问它,而静态数据成员受到访问权限的约束。约束。必须是必须是public权限时,才可能在类外进行访权限时,才可能在类外进行访问问。4、为
58、了保持静态数据成员取值的一致性,通常在、为了保持静态数据成员取值的一致性,通常在构造函数中不给静态数据成员置初值,构造函数中不给静态数据成员置初值,而是在对而是在对静态数据成员的定义性说明时指定初值静态数据成员的定义性说明时指定初值。 class Aint i;static int count;public:A(int a=0) i=a; count+; cout Number of Objects=countn; A() count-; cout Number of Objects=countn;void Show() cout i=in; cout count=countn;int A:c
59、ount;void main(void )A a1(100);A b2;a1.Show();Number of Objects=1Number of Objects=2Number of Objects=3i=100count=3Number of Objects=2Number of Objects=1Number of Objects=0静态成员函数静态成员函数可以将类的成员函数定义为静态的成员函数。即可以将类的成员函数定义为静态的成员函数。即使用关键字使用关键字static来修饰成员函数来修饰成员函数 。class A float x, y;public : A( ) static vo
60、id sum(void) . ;对静态成员函数的用法说明以下几点:对静态成员函数的用法说明以下几点:1、与静态数据成员一样,在类外的程序代码中,与静态数据成员一样,在类外的程序代码中,通过类名加上作用域操作符,可直接调用静态成通过类名加上作用域操作符,可直接调用静态成员函数员函数。 2、静态成员函数只能直接使用本类的静态数据成静态成员函数只能直接使用本类的静态数据成员或静态成员函数员或静态成员函数,但不能直接使用非静态的数但不能直接使用非静态的数据成员据成员 (可以引用使用)。这是因为静态成员函(可以引用使用)。这是因为静态成员函数可被其它程序代码直接调用,所以,数可被其它程序代码直接调用,所以,它不包含它不包含对象地址的对象地址的this指针指针。 class Tc private:int A;static int B;/静态数据成员静态数据成员public:Tc(int a)A=a; B+=a;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论