接触VC之二:MFC类基础,C++程序编写规范介绍(2)
二、类的继承
接着,要来说说类的继承。 再拿鸟类来举例子吧。鸟类还可以细分成很多类,鸡啊,鸵鸟啦,什么的。它们都是鸟类,在整体上有着共同的特征和行为,但也有其它不同点。C++类中也会存在相似的地方。怎么办?重新再写一个类,重新再写那些相同的成员吗?这时就需要继承了。我们可以写这样一个类:
class Ostrich : public Aves{}继承的写法为:
class 派生类名 : 权限关键字(在VC中一般为public) 基类名<,基类名2<…,基类名n>>
这个Ostrich类继承于Aves类,Ostrich类现在就拥有Aves类中所有的成员。如果从Ostrich类声明一对象aOstrich,就可以直接调用其aOstrich.run()。实际上就是在调用Aves类中run成员函数。现在要是在Ostrich类上添加成员的话,就是在Aves类的基础上添加成员。需要说的,在Aves类中有一个私有成员m_strBowels。因为其是私有成员,所以对于其派生类Ostrich也是不可见的。为了解决这个问题,需要将Aves类中的private关键字改为protected关键字。将m_strBowels成员描述为保护型。保护型,对其派生类是可见,对于外部和私有一样是不可见的。在现实中,鸵鸟是不会飞的,叫声也不一样,那么我们就需要更改其行为。代码如下:
class Ostrich : public Aves{public: void tweet(); void fly();}void Ostrich::tweet(){
out<<"gugugugugugugu"<<endl;}void Ostrich::fly(){ cout<<"I can''t fly by "<<m_strWing<<endl;}我们在Ostrich类的基础写了fly(),tweet()成员函数,这是与基类的成员函数名字相同。那么它们将覆盖基类的函数,如果再调用Ostrich类的fly(),tweet()函数的话则会调用我们新写的这两个函数了。基本代码如下:
#include <iostream.h>#include <string.h>class Aves{ public: Aves (); ~Aves (); void tweet(); void run(); void fly(); char m_strHead[10]; char m_strTrunk[10]; char m_strCrura[10]; char m_strWing[10];protected: char m_strBowels[10];}; Aves::Aves(){ strcpy(m_strHead, "Head"); strcpy(m_strTrunk, "Trunk"); strcpy(m_strCrura, "Crura"); strcpy(m_strWing, "Wing"); strcpy(m_strBowels, "Bowels"); cout<<"a bird born!"<<endl;}Aves::~Aves(){ cout<<"a bird die!"<<endl;}void Aves::tweet(){ cout<<"jijijijijijiji"<<endl;}void Aves::run(){ cout<<"I can run by "<<m_strCrura<<endl;}void Aves::fly(){ cout<<"I can fly by "<<m_strWing<<endl;}class Ostrich : public Aves{public: void tweet(); void fly();};void Ostrich::tweet(){ cout<<"gugugugugugugu"<<endl;}void Ostrich::fly(){ cout<<"I can''t fly by "<<m_strWing<<endl;}void main(){ { Aves bird; bird.fly(); bird.run(); bird.tweet(); } cout <<"====================="<<endl; { Ostrich aOstrich; aOstrich.fly(); aOstrich.run(); aOstrich.tweet(); }}在主函数中我加多加了两对大括号,请大家分析一下bird,aOstrich生存区域。 以上是一个单继承的例子,至于多继承解释理论是一样。大家可以自己尝试。在后的第七部分中的COM编写中将出现多继承的现象。 在继承派生还记得,派生类对象也是其基类的对象,基类的指针是可以指向派生类的对象的。如我们有是一个Aves *lpBird;指针,那么我们写lpBird=&aOstrich是合法的,因为鸵鸟也是一种鸟。 现在,要提到类最后的一个重要概念就是虚成员函数。
三、虚函数
上一段文字里说到一个基类指针可以指向一个派生类的对象。如果当lpBird指向了aOstrich,那么调用lpBird->fly();的结果会是什么呢?哇,是”I can fly by Wing”,快来看呐,我们指的那只鸟居然会飞了!显然这是我们不希望看到的结果。为了解决这个问题,我在Aves类声明体中将所有成员函数定义为virtual虚函数。
class Aves{ public: Aves (); ~Aves (); virtual void tweet(); virtual void run(); virtual void fly(); char m_strHead[10]; char m_strTrunk[10]; char m_strCrura[10]; char m_strWing[10];protected: char m_strBowels[10];};再试试看,结果成为我们要的” I can’t fly by Wing”了。为什么呢?是这样的。当一个类中有虚函数(包括基类含有的)的时候,会给这个类的所有虚函数建立起一个表,函数名与函数地址的映射(包括基类的虚函数)。当对象执行一个虚函数时,则系统先会查这个虚函数表(vtable),找到这个函数名对应的函数地址,调用它。当在派生类添加了与基类虚函数同名的函数,系统会自动将其设定为虚函数。并将这个函数地址改写到虚函数表中。如果再调用这个虚函数时,就会调用新添加的虚函数。像上面的例子,当调用lpBird->fly()时,系统会先查lpBird指向对象的虚函数表,而不会不管三七二十一地直接调用其本类的函数。示例代码如下: