在学习c++多态时,心里总是有些疑惑:什么是多态?什么是虚函数?
开始并不在意,但是在学习抽象类时问题出现了。所以现在又翻回头来重新咀嚼一下多态以及虚函数。
参考文章:C++ 虚函数经典深入解析 c++虚函数详解
何为多态?
简而言之,就是让父类拥有多种形态,通过父类来实例化子类对象,然后用父类对象调用子类成员函数。
而要实现这些,一个很重要的工具就是虚函数(函数的隐藏与覆盖)。
下面看个例子来理解一下虚函数:
#include <iostream>
#include <list>
#include <string>
using namespace std;
class Employee
{
public:
string strName;
Employee()
{
}
Employee(string strName)
{
name = strName;
}
virtual void PrintE()
{
cout<<name<<" is a employee"<<endl;
}
private:
string name;
};
class Manger : public Employee
{
public:
Manger(string strname)
{
name = strname;
}
void PrintE()
{
cout<<name<<" is a manger"<<endl;
}
private:
string name;
};
int main()
{
Employee *ep = new Employee("Bill");
Employee *mg = new Manger("john");
list<Employee*> lstEp;
lstEp.push_back(ep);
lstEp.push_back(mg);
for (list<Employee*>::iterator iter = lstEp.begin(); iter != lstEp.end(); iter++)
{
(*iter)->PrintE();
}
delete ep;
delete mg;
getchar();
}
父类employee下有子类manger,如果对于manger函数来说,通过父类实例化的子类对象调用不加virtual的函数printE时,实际上调用的父类的printE函数,如果想要调用子类的函数,则需要给父类的函数加上关键字virtual。
我们在从原理上理解一下:
了解c++的人应该明白:虚函数的实现依赖于虚函数表,对于c++来说,虚函数表就是一个类中虚函数的地址表,学习过c语言的应该知道函数指针,函数在内存中实际上是一段二进制代码,函数指针则是二进制代码在内存中的起始地址。而虚函数表中存放的是所有虚函数的函数指针。
可以这样理解:
通过父类实例化子类对象后得到一个子类对象,而子类对象的首地址则是虚函数表的地址(指针4个字节)。
circle是子类,shape是父类。
大家可以想一下,如果在父类中不将calcArea计算面积函数定义为虚函数,在子类中,该函数将被继承下来,而在通过对象调用时当然调用的是父类的calcArea函数
我们可以看到最后的结果是:shape
而如果将父类的calcArea函数定义为virtual,而子类中对calcArea函数进行了不同的实现,所以子类中虚函数表的虚函数指针所指向的函数不一样。
所以当我们用父类的指针指向子类的对象时,他就会通过子类的虚函数表找到子类中的虚函数,从而实现多态。
而为什么子类的函数不加virtual也会默认是虚函数呢?
因为它继承了父类。
这就是本篇文章的全部内容。