C++之多态

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

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_base 24 { 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和基类指针ptr
18     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 list
116 
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值