C++多态性 可以简单概括为“一个借口,多种方法”,程序在运行时才决定调用那个函数,这也是C++面对对象编程的核心。
C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义 父类的方式叫 覆盖也称重写
(提醒下:重写有两种:重写成员函数(这叫隐藏),重写虚函数,只有重写虚函数才能算真正体现C++多态性)。
参考:
多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。
参考:
C++有三大特性:封装,继承,多态
封装实现了代码模块化;继承可以扩展已存在的代码 -------> 他们都是为了实现代码的重用
多态的目的是为了借口重用,也即是,不论传递来的究竟是哪个类对象,函数都能通过同一个接口调用适应各自对象的实现方法。
最常用的方法:声明基类的指针,利用该指针 指向一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。
举例;
#include<iostream>
using namespace std;
class A //基类
{
public:
void foo()
{
cout << 1 << endl;
}
virtual void fun() //虚函数
{
cout << 2 << endl;
}
};
class B : public A //派生类
{
public:
void foo() //隐藏
{
cout << 3 << endl;
}
void fun() //重写
{
cout << 4 << endl;
}
};
int main(void)
{
A a;
B b;
A *p = &a; //指向A(基类)
p->foo(); // 调用A(基类)的foo() 输出1
p->fun(); // 调用A(基类)的fun() 输出2
p = &b; // 指向B(派生类)
p->foo(); //指向A 的foo() 输出1
p->fun(); //指向B 的fun() 输出4
return 0;
}
解析:其实,第一个p-->foo() 和 p-->fun() 很好理解,本身是基类,指向的也是基类的对象,调用本身的函数。
第二个p-->foo() 和 p-->fun() 分别输出 1 4 ; p指向的是子类对象,这就体现了多态的用法。在子类中 重写了fun()函数 ,所以调用重写后的fun()函数。 p--->fun() 调用过程:p->fun()指针是基类指针,指向的fun是一个虚函数,由于每个虚函数都有一个虚函数列表,此时p调用fun()并不是直接调用函数,而是通过虚函数列表找到相应的函数的地址,因此根据指向的对象不同,函数地址也将不同,这里将找到对应的子类的fun()函数的地址,因此输出的结果也会是子类的结果4。
上面的测试方法还可以换成 :
B*ptr = (B*)&a ; ptr->foo() ; ptr->fun(); 这也很好理解,将基类对象强制转换成派生类对象,但是这样可能导致访问超过基类的大小时,会崩溃的。