C|基类虚函数+基类对象指针指向派生类对象实现运行时的多态

内容分享11小时前发布
0 0 0

1 继承性

继承是面向对象程序设计的一个重大特征,它允许在已有类的基础上创建新的类。

  • 基类、父类

  • 派生类、导出类或子类

继承可以让程序员在已有类的基础上通过增加或修改少量代码的方法得到新的类,从而较好地解决代码重用的问题。

派生类的定义:

class 派生类名:派生方法 基类名

{//派生类新增的数据成员和成员函数

};

派生类的成员函数不能访问基类的私有数据成员。

派生类是基类的扩展,可以是保存的数据内容的扩展,也可以是功能的扩展。

当派生类对基类的某个功能进行扩展时,他定义的成员函数名可能会和基类的成员函数名重复。

如果只是函数名一样,而原型不同时,系统认为派生类中有两个重载函数。如果原型完全一样,则派生类的函数会覆盖基类的函数。这称为重定义基类的成员函数。

将派生类对象赋给基类对象:派生类中的基类部分赋给此基类对象,派生类新增加的成员就舍弃了。

基类指针指向派生类对象:尽管该指针指向的对象是一个派生类对象,但由于它本身是一个基类的指针,它只能解释基类的成员,而不能解释派生类新增的成员。因此,只能访问派生类中的基类部分。

基类的对象引用派生类的对象:从此基类对象看到的也是派生类中的基类部分。

2 多态性

多态性是指由于继承而关联在一起的不同类的对象,对于一样的成员函数调用做出不同反应的能力。多态性是通过virtual函数和动态绑定实现的。当通过基类指针或引用请求使用一个virtual函数时,C++在与对象相关的合适的派生类中选择正确的重载函数。通过virtual函数和多态性使用,一个成员函数调用根据接收该调用对象的类型不同做出不同的反应。

不同对象收到一样的消息时产生不同的动作。即用一个名字定义不同的函数。

  • 静态联编:编译时已决定用哪一个函数实现某一动作。

  • 动态联编:直到运行时才决定用哪一个函数来实现动作。

2.1 静态联编

函数重载是用同一名字实现访问一组相关的函数。

重载函数是通过“名字压延”方法来实现。即在编译时将函数名和参数结合起来创造一个新的函数名,用新的名字替换原有名字。

2.2 运算符重载

我们知道,基础数据类型所能实施的数据操作就是其可用的运算符。相应的,用户自定义类型也可以重载一些运算符,实现如基础数据类型同样的用运算符进行的操作。

2.3 运行时多态性

运行时多态性是指必须等到程序动态运行时才可确定的多态性,主要通过继承结合动态绑定获得。这与类的继承密切相关。由于存在类型的兼容性,所以有些函数只有在运行时才能确定是调用父类的还是子类的函数。在C++中,使用虚函数(Virtual Functions)来实现。虚函数提供动态重载方式,允许函数调用与函数体之间的联系在运行时才建立。

在类的继承层次体系下的多态性,是利用基类的指针句柄和基类的引用句柄而不是利用名字句柄实现的。

把一个成员函数声明为virtual函数,将导致程序根据句柄指向的对象的类型,而不是根据句柄类型,动态地决定要调用的函数版本。当一样的消息(函数)发送给(通过基类指针)此基类在继承层次中不同的派生类对象时,将会呈现出“多种形式”,这就是多态性的行为。

多态性通过三级指针的数据结构来实现。当C++编译含有一个或多个virtual函数的类时,会为这个类创建一个virtual函数表,是一个包含函数指针的数组,当调用virtual函数时,这些指针指向实际执行的函数。

多态性可以让程序员进行“通用化编程”而不是“特殊化编程”,并且让程序更具扩展性。

00:00

虚函数的定义:在基类中用关键词virtual说明,并在派生类中重新定义的函数称为虚函数。在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数与参数类型的顺序都必须与基类中的原型完全一样。

当把一个函数定义为虚函数时,等于告知编译器,这个成员函数在派生类中可能有不同的实现。必须在执行时根据传递的参数来决定调用哪一个函数。

class Shape {

public:

virtual void printShapeName() {cout<<“Shape”<<endl;}

};

class Point:public Shape {

public:

virtual void printShapeName() {cout<<“Point”<<endl;}

}

class Circle:public Point {

public:

virtual void printShapeName() {cout<<“Circle”<<endl;}

}

class Cylinder:public Circle {

public:

virtual void printShapeName() {cout<<“Cylinder”<<endl;}

}

将派生类对象赋给基类对象,并不能产生多态效果:

int i;

Point aPoint;

Circle aCircle;

Cylinder aCylinder;

Shape shapes[3]= {aPoint, aCircle, aCylinder};

for (i=0;i<3;i++)

shapes[i].printShapeName();

运行结果:

Shape

Shape

Shape

基类指针指向派生类对象的一样名字的函数,依赖每个对象自己做出恰当的响应:

int i;

Point aPoint;

Circle aCircle;

Cylinder aCylinder;

Shape *pShape[3]= { &aPoint, &aCircle,&aCylinder };

for (i=0;i<3;i++)

pShape[i]->printShapeName();

运行结果:

Point

Circle

Cylinder

基类的对象引用派生类的对象:

int i;

Point aPoint;

Circle aCircle;

Cylinder aCylinder;

//Shape *pShape[3]= {&aPoint, &aCircle, &aCylinder};

Shape &shape1= aPoint;

//for (i=0;i<3;i++) pShape[i]->printShapeName();

shape1.printShapeName();

运行结果:

Point

使用virtual函数和多态性,可能设计和实现更具扩展性的系统。程序员编写的程序可以处理在开发阶段不存在的对象类型。

© 版权声明

相关文章

暂无评论

none
暂无评论...