C++之多态

来源:互联网 发布:app软件大全 编辑:程序博客网 时间:2024/04/30 09:59

多态性就是不同对象收到相同消息时,产生不同的动作。用一个名字定义不同的函数,这些函数执行不同但又类似的操作,即用同样的接口访问功能不同的函数,实现“一个接口,多种方法”。

C++支持的多态性分为编译时多态性和运行时多态性。其中编译时多态性主要通过函数重载和操作符重载来实现,而运行时多态通过继承和虚函数来实现。

  1虚函数

  1.1引入派生类后的对象指针

使用:

复制代码
 1 #include<iostream> 2 using namespace std; 3  4 class my_base  5 { 6  int a,b; 7 public: 8  my_base(int x,int y) 9  {10      a=x;11      b=y;12  }13 14  void show()15  {16 17   cout<<"my_base--------"<<endl;18   cout<<a<<" "<<b<<endl;19  }20 21 };22 23 class my_class:public my_base24 {25 26      int c; 27     public:28 29  my_class(int x,int y,int z):my_base(x,y)30  {31 32      c=z;33  }34 35  void show()36  {37 38       cout<<"my_class----------"<<endl;39       cout<<"c="<<c<<endl;40  }41 };42 43 void main()44 {45 46      my_base mb(50,50),*ptr;47      my_class mc(10,20,30);48      ptr=&mb;49      ptr->show ();50      ptr=&mc;51      ptr->show ();52 53 }
复制代码

运行结果如下:

     

  引入派生类后,使用对象指针应该主义的几个问题:1)声明为指向基类对象的指针可以指向它的公有派生对象,但不允许指向它的私有派生的对象。2)不能将一个声明为指向派生类对象的指针指向其基类的对象。3)声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问从基类继承来的成员,而不能直接访问公有派生类中定义的成员。

复制代码
 1 对于3)的说明: 2  3 Class A{//... 4 Public: 5     Void print1(); 6 }; 7  8 Class B: Public  A{//.... 9 10     Public:11     Void print2();12 };13 14 Void main()15 {16 17     A  op1,*ptr;//定义基类A的对象op1和基类指针ptr18     B  op2;19     Ptr=&op1;20     Ptr->print1();21     Ptr=&op2;22     Ptr->print1();23     Ptr->print2(); //错误,不能访问派生类中定义的成员函数print2()24 }
复制代码

  如果需要访问其公有派生类中的特定成员,可以将基类指针用显式类型转换为派生类指针。错误语句可以改写为:((B*)ptr)->print2();  外层括号表示对ptr强制转换,而不是返回类型。

1.2虚函数的定义和使用

使用虚函数,实现动态调用功能。多态性实现了在基类定义派生类所拥有的通用接口,而在派生类定义具体的实现方法。虚函数的定义在基类中进行,在需要定义为虚函数的成员函数的声明中冠以virtual关键字,从而提供一种接口界面。

定义方法:

Virtual 函数类型 函数名(形参列表)

{函数体}  

复制代码
  1  #include<iostream>  2   3 using namespace std;  4   5 class parent  6   7 {  8   9 protected: 10  11  char version; 12  13 public: 14  15  parent() 16  17  { 18  19   version='A'; 20  21  } 22  23  virtual void print() 24  25  { 26  27   cout<<endl<<"The parent.version  "<< version; 28  29  } 30  31 }; 32  33 class derived1:public parent 34  35 { 36  37 private : 38  39  int info; 40  41 public: 42  43  derived1(int number) 44  45  { 46  47   info=number; 48  49   version='1'; 50  51  } 52  53  void print() 54  55  { 56  57   cout<<endl<<"The derived1 info:"<<info<<" version "<<version; 58  59  } 60  61 }; 62  63 class derived2:public parent 64  65 { 66  67 private : 68  69  int info; 70  71 public: 72  73  derived2(int number) 74  75  { 76  77   info=number; 78  79  } 80  81  void print() 82  83  { 84  85   cout<<endl<<"The derived2 info:"<<info<<" version "<<version<<endl; 86  87  } 88  89 }; 90  91 void main() 92  93 { 94  95  parent ob,*op; 96  97  op=&ob; 98  99  op->print ();100 101  derived1 d1(3);102 103  derived2 d2(15);104 105  op=&d1;106 107  op->print ();108 109  op=&d2;110 111  op->print ();112 113 }
复制代码

关于虚函数定义的几点说明:

1)在基类中利用关键字virtual可以将public和protected部分的成员函数声明为虚函数;

2)在派生类中对虚函数进行重新定义时,可以写virtual也不可以不写,当不写时遵循三个规则来判断(名称、参数个数和类型、返回类型);

3)虚函数重新定义时其函数原型必须与基类中全完相同;

4)只有通过基类指针访问虚函数时才能获得运行时的多态性,使用对象名和点运算符的调用方式是在编译时的静态连编;

5)一个虚函数无论被公有继承多少次,仍然保持其虚函数的特性;

6)虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态成员函数。但虚函数可以在另一个类中被声明为友元函数;

7)构造函数不能是虚函数,但是析构函数可以。

虚函数与重载函数的区别:当普通函数重载时,其函数的参数或参数类型必须有所不同,函数的返回类型也可以不同。但是当重载一个虚函数时要求其原型完全相同。如果仅仅返回类型不同,其余相同,系统会给出出错信息;如果仅仅函数名相同,参数的个数类型或顺序不同,系统将它作为普通的函数重载,从而丢失虚函数的特性。

·多重继承与虚函数

复制代码
 1 #include <iostream.h> 2  3 Class base1{ 4  5 Public: 6  7 Virtual void fun()    //定义为虚函数 8  9 {cout<<”--base1--”<<endl;} 10 11 };12 13 Class base2{14 15 Public:16 17  void fun()//定义为普通成员函数18 19 {cout<<”--base2--”<<endl;} 20 21 };22 23 Class derived:public base1,public base2{24 25 Public:26 27 Void fun()28 29 {cout<<”--derived--”<<endl;}30 31 };32 33  34 35 Void main()36 37 {38 39 Base1 *ptr1;40 41 Base2 *ptr2;42 43 Derived obj3;44 45 Ptr1=&obj3;46 47 Ptr1->fun();//此处调用派生类derived的fun()48 49 Ptr2=&obj3;50 51 Ptr2->fun();//此处调用的是基类base2的fun()52 53 }
复制代码

  1.3纯虚函数和抽象类

纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但要求在它的派生类中定义自己的版本,或重新说明为虚函数。

纯虚函数的一般形式:

Virtual 函数类型 函数名(参数表)=0;

如果一个类至少有一个纯虚函数,那么就称该类为抽象类。关于抽象类使用的一些注意:1)抽象类只能用作其他类的基类,不能建立抽象类对象;2)抽象类不能用作参数类型、函数类型或显示转换的类型,但可以声明指向抽象类的指针或引用,该指针可以指向它的派生类。3)如果在抽象类的派生类中没有重新说明纯虚函数,而派生类只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。4)在抽象类中也可以定义普通成员函数或虚函数,虽然不能为抽象类声明对象,但仍然可以通过派生类对象来调用这些不是纯虚函数的函数。5)不允许从具体类派生出抽象类。

程序示例:在这个程序中,建立两种类型的表,队列和栈。虽然两个表完全不同,但他们可以用同一个接口访问。

复制代码
  1 #include<iostream.h>  2   3 #include<stdlib.h>  4   5 #include<ctype.h>  6   7 Class list  8   9 { 10  11 Public: 12  13 List *head; 14  15 List *tail; 16  17 List *next; 18  19 Int num; 20  21 List() 22  23 { 24  25 Head=tail=next=NULL; 26  27 } 28  29 Virtual void store(int i)=0; 30  31 Virtual void retrieve()=0; 32  33 }; 34  35 Class queue:public list 36  37 { 38  39 Public: 40  41 Void store(int i); 42  43 Int retrieve(); 44  45 }; 46  47   48  49 Void queue::store(int i) 50  51 { 52  53 List *item; 54  55 Item=new queue; 56  57 If(!item) 58  59 { 60  61 Cout<<”Allocation error”<<endl; 62  63 Exit(1); 64  65 } 66  67 Item->num-i; 68  69 If(tail)tail->next=item; 70  71 Tail=item; 72  73 Item->next=NULL; 74  75 If(!head) 76  77 Head=tail; 78  79 } 80  81   82  83 Int queue::retrieve() 84  85 { 86  87 Int i; 88  89 List *p; 90  91 If(!head) 92  93 { 94  95 Cout<<”list empty”<<endl; 96  97 Return 0; 98  99 }100 101 I=head->num;102 103 P=head;104 105 Head=head->next;106 107 Delete p;108 109 Return i;110 111 }112 113  114 115 Class stack:public list116 117 {118 119 Public:120 121 Void store(int i);122 123 Int retrieve();124 125 };126 127 Void stack::store(int i)128 129 {130 131 List *item;132 133 Item=new stack;134 135 If(!item)136 137 {138 139 Cout<<”Allocation error”<<endl;140 141 Exit(1);142 143 }144 145 Item->num=i;146 147 If(head)item->next=head;148 149 Head=item;150 151 If(!tail)152 153 tail=head;154 155 }156 157  158 159 Int stack::retrieve()160 161 {162 163 Int i;164 165 List *p;166 167 If(!head)168 169 {170 171 Cout<<”list empty”<<endl;172 173 Return 0;174 175 }176 177 I=head->num;178 179 P=head;180 181 Head=head->next;182 183 Delete p;184 185 Return i;186 187 }188 189  190 191 Main()192 193 {194 195 List *p;196 197 Queue q_ob;198 199 P=&q_ob;200 201 P->store(1);202 203 P->store(2);204 205 P->store(3);206 207 Cout<<”queue:”;208 209 Cout<<p->retrieve();210 211 Cout<<p->retrieve();212 213 Cout<<p->retrieve();214 215 Cout<<endl;216 217 Stack s_ob;218 219 P=&s_ob;220 221 P->store(1);222 223 P->store(2);224 225 P->store(3);226 227 Cout<<”stack:”;228 229 Cout<<p->retrieve();230 231 Cout<<p->retrieve();232 233 Cout<<p->retrieve();234 235 Cout<<endl;236 237 Return 0;238 239 }
复制代码

运行结果:

Queue:123

Stack:321

0 0
原创粉丝点击