接上段.
我们在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指向对象的虚函数表,而不会不管三七二十一地直接调用其本类的函数。示例代码如下:
#include <iostream.h>
#include <string.h>
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];
};
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();
Aves *lpBird;
lpBird = &aOstrich;
lpBird->fly();
}
}
关于虚函数更具体的情况,请参看vckbase第12期的《解析动态联编》。关于其它C++语法,请自行查看C++教材。
那么MFC类呢?简单地来说,MFC类只是将许多有关联的API函数将其封装在一起。在WinSDK中WinAPI都是一些零零散散的函数,它们大部分中都会有一个参数是它的服务对象的句柄。比如,CreateWindow函数会需要一个句柄输出来返回一个窗口句柄来表达其建立的窗口对象,ShowWindow函数需要一个窗口句柄来指定哪一个窗口要改变显示状态,CloseWindow函数需要一个窗口句柄来指定哪一个窗口要被关闭。可以理解为是句柄在围绕着函数转,句柄在以函数为中心。而MFC是将几个若干有服务关联的函数封装在一起成为成员函数,每一个类会有一个保护的句柄成员变量来保存当前类对象所代表的服务对象,在对外调用上看就可以将其类对象看成其服务对象,这些成员函数就可以看成其服务对象本来固有的方法。在使用上比使用WinAPI更为形象和理解。下面做一个比照的例子.