1.多态性的概念:
多态性是面向对象程序设计的重要特性。利用多态性可以设计和实现一个易于扩展的系统。
在C++中,多态性是指具有不同功能的函数用同一个函数名,即用同一函数名调用不同内容的函数。
现实生活中有很多多态性的例子。
多态性的一般表述:向不同的对象发送同一消息(调用函数),不同的对象会产生不同的行为(方法)。
例如,运算符+调用operator+函数,对不同类型数据的操作互不相同。
从系统实现的角度看,多态性分为两类:
①静态多态性:系统在编译的时候就能知道要调用的是哪个函数。也叫做编译时的多态性。(如函数重载)
②动态多态性:程序在运行过程中才动态地确定操作所针对的对象。又叫做运行时的多态性。
2.静态联编和动态联编:
静态联编:
对程序中出现的标识符进行的绑定和解析过程,在产生目标代码的编译时就固定下来。
静态联编又称为静态绑定和早期绑定。
#include
using namespace std;
class Point
{private:
double x, y;
public:
Point(double i, double
j)
{
x=i;
y=j;
}
double Area()
const
{
return
0.0;
}
};
class Rectangle:public
Point
{ public:
Rectangle(double i, double j, double k, double
l);
double Area() const
{
return
w*h;
}
private:
double w,h;
};
Rectangle::Rectangle(double i, double j, double
k, double l) :Point(i,j)
{
w=k;
h=l;
}
void fun(Point &s)
//一般函数,参数是基类引用
{
cout<<"Area="<<s.Area()<<endl;
}
int main()
{
Rectangle
rec(3.0, 5.2, 15.0, 25.0);
fun(rec);
return
0;
}
什么是动态联编:
根据目标对象的动态类型(而不是静态类型),在程序运行时(而不是在编译时)将函数名绑定到具体的函数实现上。
动态联编又称为动态绑定和晚期绑定。
动态联编的实现——虚函数:
C++提供了virtual关键字用于指定函数是否为虚函数。如果在一个函数声明前面加上virtual,则这个函数为虚函数,系统将对其进行动态联编。
动态联编的实现过程:
在程序编译时:
①为每一个有虚函数的类设置一个虚函数表v_table,一个指针数组,存放每个虚函数的入口地址。
②在函数调用处插入一个隐藏的,指向虚函数表的指针v_pointer;
在程序运行中:
根据对象的v_pointer,在相应的虚函数表中获得函数入口,来调用正确的函数。
3.虚函数的定义与使用:
虚函数的定义方法:
①
在基类中以关键字virtual说明;
virtual
<</span>函数返回类型>
<</span>函数名>(<</span>参数表>)
如: virtual double
Area( );
② 在派生类中重新定义一个同名非静态成员函数。
3点说明:
A.派生类中同名函数必须与基类虚函数完全一致(即函数名、参数个数与类型、返回类型都相同)。
B.采用基类对象指针或引用来调用虚函数。
C.派生类必须以公用方式继承。
只有满足上面3点,程序才会按动态联编的方式调用函数,否则虚函数将按静态联编的方式调用。
#include< iostream >
using namespace std;
class Point
{ private:
double x, y;
public:
Point(double
i,
double j) {x=i; y=j;}
virtual
double
Area() const
{ return
0.0;}
};
class Rectangle:public
Point
{ public:
Rectangle(double
i, double j, double k, double
l);
virtual
double Area()
const
{return w*h;}
private:
double w,h;
};
Rectangle::Rectangle(double
i, double j, double k, double
l) :Point(i, j) { w=k; h=l; }
void
fun(Point
&s)
//一般函数,参数是基类引用
{ cout<<"Area="<<s.
Area()<<endl; }
int main()
{
Rectangle rec(3.0, 5.2, 15.0, 25.0);
fun(rec);
return 0;
}
运行结果:
Area=375
总结虚函数的使用方法:
① 在基类用virtual声明成员函数为虚函数;
② 在派生类中重新定义此函数;
C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。
③定义一个指向基类的指针(或引用);
④通过重新对该指针(或引用)做同类族对象赋值,调用该类对象同名虚函数。
虚函数与指向基类的指针(引用)变量配合使用,就能方便地调用同一类族中不同类对象的同名函数。从而实现运行时的多态性。
在什么情况下应该声明虚函数:
① 函数所在的类会作为基类,并且有更改功能的需要;
② 对于继承后不需要更改功能的函数不要声明为虚函数;
③ 考虑对成员函数的调用是否通过指针或引用;
虚函数的意义:
如果说:数据封装使得代码模块化; 继承实现了代码重用;
那么,虚函数采用动态联编技术造就了程序设计的多态性——接口重用。
虚析构函数:
﹡如果基类的析构函数为虚析构函数,派生类的析构函数也均为虚析构函数。
﹡如果基类的析构函数为虚析构函数,无论基类指针指的是哪一个派生类对象,当该对象撤销时,系统都会采用动态联编,调用相应的析构函数。
程序中最好把析构函数声明为虚函数!即使基类不需要析构函数,也显式地定义一个函数体为空的虚析构函数。
构造函数不能声明为虚函数!!!
4.
纯虚函数和抽象类:
对于物理上无法实现而逻辑上又不得不存在的抽象的虚函数,可以将其在基类中用不包括任何代码的纯虚函数来定义。
纯虚函数的定义方法:
virtual
< 函数返回类型 >
< 函数名 >( 参数表 )=0
---------------------------------------------------
virtualchar *Type() const { return NULL;
}
//虚函数
virtualdouble Volume() const { return
0;}
//虚函数
virtualdouble SurfaceArea() const { return 0;
}
//虚函数
---------------------------------------------------
virtual
char *Type() const
=
0;
//纯虚函数
virtual
double Volume() const
=
0;
//纯虚函数
virtual
double SurfaceArea() const =
0;
//纯虚函数
---------------------------------------------------
抽象类:
包含一个或多个纯虚函数的类叫做抽象类。
有关抽象类:
① 不能创建抽象类的对象;
②
抽象类不能作为参数类型、函数返回和显式转换;
③
可以定义指向抽象类的指针;
④
不能调用抽象类的纯虚函数,它仅仅是为派生类提供的一个公共接口;
C++采用抽象类,将接口和实现分离开来。