//取一个对象的地址(或指针或引用),并看作基类的地址,这被
称为向上映射,因为继承树是以基类为顶点的。
关键字:向上映射,早捆绑,晚捆绑
一 看以下程序
#include<iostream.h>
class jilei
{
public:
void Draw();
};
void jilei::Draw()
{
cout<<"基类Draw"<<endl;
}
class paishenglei:public jilei
{
public:
void Draw();
};
void paishenglei::Draw()
{
cout<<"派生类Draw"<<endl;
}
void Play(jilei &j)
{
j.Draw();
}
void main(void)
{
jilei jl;
paishenglei psl;
Play(jl);
Play(psl); //向上映射,但输出为“基类Draw”
}
过运行这个程序看到,输出是“基类Draw”。显然,这不是所希望的输出,因为
我们知道这个对象实际上是派生类而不只是一个基类。这个调用应当输出“派生类Draw”
。为此,由基类派生的任何对象应当使它的Draw版本被使用。然而,当对函数用C方法时,
以上的标现并不奇怪。为了理解这个问题,需要知道捆绑的概念。
二 函数调用捆绑:
把函数体与函数调用相联系称为捆绑(binding)。当捆绑在程序运行之前(由编译
器和连接器)完成时,称为早捆绑。我们可能没有听到过这个术语,因为在过程语言中是
不会有的:C编译只有一种函数调用,就是早捆绑。上面程序中的问题是早捆绑引起的,因
为编译器在只有jilei地址时它不知道正确的调用函数。解决方法被称为晚捆绑,这意味着
捆绑在运行时发生,基于对象的类型。晚捆绑又称为动态捆绑或运行时捆绑。当一个语言
实现晚捆绑时,必须有一种机制在运行时确定对象的类型和合适的调用函数。这就是,编
译器还不知道实际的对象类型,但它插入能找到和调用正确函数体的代码。晚捆绑机制因
语言而异,但可以想象,一些种类的类型信息必须装在对象自身中。稍后将会看到它是如
何工作的。
三 虚函数
对于特定的函数,为了引起晚捆绑, C + +要求在基类中声明这个函数时使用
virtual关键字。晚捆绑只对virtual起作用,而且只发生在我们使用一个基类的地址时,
并且这个基类中有virtual函数,尽管它们也可以在更早的基类中定义。为了创建一个
virtual成员函数,可以简单地在这个函数声明的前面加上关键字virtual。对于这个函数
的定义不要重复,在任何派生类函数重定义中都不要重复它(虽然这样做无害)。如果一
个函数在基类中被声明为virtual,那么在所有的派生类中它都是virtual的。在派生类中
virtual函数的重定义通常称为越位。
为了在上面的程序中得到所希望的结果,只需简单地在基类中的Draw() 之前加关
键字:virtual.
如下所示:
#include<iostream.h>
class jilei
{
public:
virtual void Draw();
};
void jilei::Draw()
{
cout<<"基类Draw"<<endl;
}
class paishenglei:public jilei
{
public:
void Draw();
};
void paishenglei::Draw() //只需对基类编程
{
cout<<"派生类Draw"<<endl;
}
void Play(jilei &j)
{
j.Draw();
}
void main(void)
{
jilei jl;
paishenglei psl;
Play(jl);
Play(psl); //向上映射,输出为“派生类Draw”
}