多态的基本概念
实现了多态机制的程序,可以使用同一个名字完成不同的功能。
多态分为编译时多态和运行时多态。
多态
静态多态在编译期间就可以确定函数的调用地址,并产生代码。
函数调用与代码入口地址的绑定需要在运行时刻才能确定,称为动态联编或动态绑定。
在类之间满足赋值兼容的前提下,实现动态绑定必须满足以下两个条件
- 必须声明虚函数
- 通过基类类型的引用或者指针调用虚函数。
虚函数
所谓虚函数,就是在函数声明时前面加了virtual关键字的成员函数。
声明虚函数成员的格式
virtual 函数返回值类型 函数名(形参表);
通过基类指针实现多态
程序6-1 通过基类指针实现多态示例
#include
using namespace std;
class A
{
public:
virtual void Print()
{
cout<<"A::Print"<Print(); //多态,目前指向基类对象,调用a.Print(),输出A::Print
pa = pb; //派生类指针赋给基类指针,pa指向派生类对象b
pa->Print(); //多态,目前指向派生类对象,调用b.Print(),输出B::Print
pa = &d; //基类指针pa指向派生类对象d
pa->Print(); //多态,目前指向派生类对象,调用d.Print(),输出D::Print
pa = &e; //基类指针pa指向派生类对象e
pa->Print(); //多态,目前指向派生类对象,调用e.Print(),输出E::Print
return 0;
}
A::Print
B::Print
D::Print
E::Print
程序6-2 用基类指针访问基类对象及派生类对象
#include
#include
using namespace std;
class A
{
public:
void put_name(string s)
{
name = s;
}
virtual void print_name() const
{
cout<<"A::"<put_name("多态示例_名字"); //赋给基类对象A_obj
cout<<"A_p->print_name的输出内容:\t";
A_p->print_name(); //使用指针输出,A_obj中的值
cout<<"A_obj.print_name()的输出内容:\t";
A_obj.print_name(); //使用对象输出
A_p = &B_obj; //基类指针指向派生类对象
A_p->put_name("另一个名字"); //多态,赋给派生类对象B_obj
cout<<"A_p->print_name()的输出内容:\t";
A_p->print_name(); //多态,使用指针输出,B_obj中的值,继承的name的值
cout<<"B_obj.print_name()的输出内容:\t";
B_obj.print_name(); //使用对象输出,B_obj中的值,继承的name值
B_obj.put_phone("电话号码999"); //派生类对象赋值
cout<<"((B *)A_p)->print_phone()的输出内容:\t";
((B*)A_p)->print_phone(); //强制转换基类指针,输出的是派生类对象中的值
cout<<"B_obj.print_phone()的输出内容:\t\t";
B_obj.print_phone(); //输出的是派生类对象中的值
return 0;
}
A_p->print_name的输出内容: A::多态示例_名字
A_obj.print_name()的输出内容: A::多态示例_名字
A_p->print_name()的输出内容: B::另一个名字,另一个名字
B_obj.print_name()的输出内容: B::另一个名字,另一个名字
((B *)A_p)->print_phone()的输出内容: 电话号码999
B_obj.print_phone()的输出内容: 电话号码999
通过基类引用实现多态
通过基类指针调用虚函数时可以实现多态,通过基类的引用调用虚函数的语句也是多态的。
程序6-3 基类引用实现多态
#include
using namespace std;
class A
{
public:
virtual void Print()
{
cout<<"A::Print"<
*多态的实现原理
多态的关键在于通过基类指针或引用调用一个虚函数时,编译阶段不能确定到底调用的时基类还是派生类的函数,运行时才能确定。
程序6-4 多态机制下对象存储空间的大小
#include
using namespace std;
class A
{
public:
int i;
virtual void func(){}
virtual void func2(){}
};
class B:public A
{
int j;
void func(){}
};
int main()
{
cout<
多态实例
在面向对象的程序设计中,使用多态能够增强程序的可扩充性。
使用多态也能起到精简代码的作用。
程序6-5 使用多态出来图形示例
#include
#include
using namespace std;
class CShape //基类:图形类
{
protected:
double acreage; //图形的面积,子类可以访问
public:
CShape()
{
// cout<<"基类构造函数"<a = a;
this->b = b;
this->c = c;
};
CTriangle()
{
// cout<<"三角形无参构造函数"<width<<",高度="<high<<",面积="<acreage<radius<<",面积="<acreage<a<<","<b<<","<c<<",面积="<acreage<>n;
for(i=0;i>c;
switch(c)
{
case 'R':case 'r': //矩形
cin>>temp1>>temp2;
pr = new CRectangle(temp1,temp2);
pr->setAcreage(pr->CalAcr());
pShapes[i] = pr;
break;
case 'C':case 'c':
cin>>temp1; //圆形
pc = new CCircle(temp1);
pc->setAcreage(pc->CalAcr());
pShapes[i] = pc;
break;
case 'T':case 't': //三角形
cin>>temp1>>temp2>>temp3;
pt = new CTriangle(temp1,temp2,temp3);
pt = new CTriangle(temp1,temp2,temp3);
pt->setAcreage(pt->CalAcr());
pShapes[i]= pt;
break;
}
}
if(n == 1) cout<<"共有"<PrintInfo();
delete pShapes[i]; //释放空间
}
return 0;
}
3
C 6
R 7.6 8.2
T 3 4 5
共有3种图形,分别是:
圆。 半径=6,面积=113.04
矩形。 宽度=7.6,高度=8.2,面积=62.32
三角形。三条边分别是:3,4,5,面积=6
多态的使用
类的成员函数直接按可以互相调用。
在普通成员函数(静态成员函数、构造函数和析构函数除外)中调用其他虚成员函数也是允许的,并且是多态的。
程序6-6 在成员函数中调用虚函数
#include
using namespace std;
class CBase
{
public:
void func1() //不是虚函数
{
cout<<"CBase::func1()"<
程序6-7 在构造函数与析构函数中调用虚函数
#include
using namespace std;
class A
{
public:
virtual void hello()
{
cout<<"A::hello"<
实现多态时,必须满足的条件是:使用基类指针或引用来调用基类中声明的虚函数。
程序6-8 多态与非多态的对比
#include
using namespace std;
class A
{
public:
void func1()
{
cout<<"A::func1"<func2(); //多态
pa->func1(); //不是多态
pb->func1(); //多态
return 0;
}
C::func2
A::func1
C::func1
虚析构函数
声明虚析构函数的格式
virtual ~类名();
虚析构函数没有返回值类型,没有参数。
如果一个类的析构函数是虚函数,则由它派生的所有子类的析构函数也是虚析构函数。
程序6-9 不使用虚析构函数的情况
#include
using namespace std;
class ABase
{
public:
ABase()
{
cout<<"ABase 构造函数"<
改写,将基类析构函数修改为虚析构函数。
virtual ~ABase()
{
cout<<"ABase 析构函数"<
纯虚函数喝抽象类
纯虚函数
纯虚函数是声明在基类中的虚函数,没有具体的定义,而由各派生类根据识记需要给出各自的定义。
声明纯虚函数的格式
virtual 函数类型 函数名(参数表) = 0;
抽象类
包含纯虚函数的类称为抽象类。
纯虚函数和函数体为空的虚函数的区别:
- 纯虚函数没有函数体,而空的虚函数的函数体为空。
- 纯虚函数所在的类是抽象类,不能直接进行实例化;而空的虚函数所在的类是可以实例化的。
共同特点是:都可以派生出新的类,然后在新类中给出虚函数的实现,而且这种新的实现具有多态特征。
程序6-10 抽象类示例
#include
using namespace std;
class A
{
private:
int a;
public:
virtual void print() = 0;
void func1()
{
cout<<"A_func1"<print();
B b;
A *pc = &b;
pc->func1();
return 0;
}
B_print
A_func1
虚基类
定义虚基类的格式
class 派生类名 : virtual 派生方式 基类名
{
派生类体
};
程序6-11 虚基类
#include
using namespace std;
class A
{
public:
int a;
void showa()
{
cout<<"a="<