- 该系列文章大部分摘自博主白鳯《C++面向对象程序设计》✍千处细节、万字总结(建议收藏)
http://t.csdn.cn/GxZ6U
- 如有不好的影响请联系删除
所谓多态性就是不同对象收到相同的消息时,产生不同的动作。这样,就可以用同样的接口访问不同功能的函数,从而实现“一个接口,多种方法”
从实现的角度来讲,多态可以划分为两类:编译时的多态和运行时的多态。在C++中,多态的实现和连编这一概念有关。所谓连编就是把函数名与函数体的程序代码连接在一起的过程。静态连编就是在编译阶段完成的连编。编译时的多态是通过静态连编来实现的。静态连编时,系统用实参与形参进行匹配,对于同名的重载函数便根据参数上的差异进行区分,然后进行连编,从而实现了多态性。运行时的多态是用动态连编实现的。动态连编时运行阶段完成的,即当程序调用到某一函数名时,才去寻找和连接其程序代码,对面向对象程序设计而言,就是当对象接收到某一消息时,才去寻找和连接相应的方法。
一般而言,编译型语言(如C,Pascal)采用静态连编,而解释型语言(如LISP)采用动态连编。静态连编要求在程序编译时就知道调用函数的全部信息。因此,这种连编类型的函数调用速度快、效率高,但缺乏灵活性;而动态连编方式恰好相反,采用这种连编方式,一直要到程序运行时才能确定调用哪个函数,它降低了程序的运行效率,但增强了程序的灵活性。纯粹的面向对象程序语言由于其执行机制是消息传递,所以只能采用动态连编。C++实际上采用了静态连编和动态连编相结合的方式。
在C++中,编译时多态性主要是通过函数重载和运算符重载实现的;运行时多态性主要是通过虚函数来实现的。
虚函数的定义是在基类中进行的,它是在基类中需要定义为虚函数的成员函数的声明中冠以关键字virtual,从而提供一种接口界面。定义虚函数的方法如下:
virtual 返回类型 函数名(形参表) {
函数体
}
在基类中的某个成员函数被声明为虚函数后,此虚函数就可以在一个或多个派生类中被重新定义。虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型的顺序,都必须与基类中的原型完全相同。
#include
#include
using namespace std;
class Family{
private:
string flower;
public:
Family(string name = "鲜花"): flower(name) { }
string getName() {
return flower;
}
virtual void like() {
cout << "家人喜欢不同的花: " << endl;
}
};
class Mother: public Family{
public:
Mother(string name = "月季"): Family(name) { }
void like() {
cout << "妈妈喜欢" << getName() << endl;
}
};
class Daughter: public Family{
public:
Daughter(string name = "百合"): Family(name) { }
void like() {
cout << "女儿喜欢" << getName() << endl;
}
};
int main() {
Family *p;
Family f;
Mother mom;
Daughter dau;
p = &f;
p->like();
p = &mom;
p->like();
p = &dau;
p->like();
return 0;
}
程序运行结果如下:
家人喜欢不同的花:
妈妈喜欢月季
女儿喜欢百合
C++规定,如果在派生类中,没有用virtual显式地给出虚函数声明,这时系统就会遵循以下的规则来判断一个成员函数是不是虚函数:该函数与基类的虚函数是否有相同的名称、参数个数以及对应的参数类型、返回类型或者满足赋值兼容的指针、引用型的返回类型。
下面对虚函数的定义做几点说明:
在一个派生类中重新定义基类的虚函数是函数重载的另一种形式。
如果在主函数中用new运算符建立一个派生类的无名对象和定义一个基类的对象指针,并将无名对象的地址赋值给这个对象指针,当用delete运算符撤销无名对象时,系统只执行基类的析构函数,而不执行派生类的析构函数。
Base *p;
p = new Derived;
delete p;
-----------------
输出:调用基类Base的析构函数
原因是当撤销指针p所指的派生类的无名对象,而调用析构函数时,采用了静态连编方式,只调用了基类Base的析构函数。
如果希望程序执行动态连编方式,在用delete运算符撤销派生类的无名对象时,先调用派生类的析构函数,再调用基类的析构函数,可以将基类的析构函数声明为虚析构函数。一般格式为
virtual ~类名(){
·····
}
虽然派生类的析构函数与基类的析构函数名字不相同,但是如果将基类的析构函数定义为虚函数,由该类所派生的所有派生类的析构函数也都自动成为虚函数。示例如下
#include
#include
using namespace std;
class Base{
public:
virtual ~Base() {
cout << "调用基类Base的析构函数..." << endl;
}
};
class Derived: public Base{
public:
~Derived() {
cout << "调用派生类Derived的析构函数..." << endl;
}
};
int main() {
Base *p;
p = new Derived;
delete p;
return 0;
}
纯虚函数是在声明虚函数时被“初始化为0的函数”,声明纯虚函数的一般形式如下:
virtual 函数类型 函数名(参数表) = 0;
声明为纯虚函数后,基类中就不再给出程序的实现部分。纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要重新定义。1
如果一个类至少有一个纯虚函数,那么就称该类为抽象类,对于抽象类的使用有以下几点规定: