最近学到C++的封装继承以及多态,对面向对象稍有了解的都知道他们的关系是互相关联着的,
今天写的主要是深究一下多态是如何实现的.
还是老规矩,上代码和内存图.
#include<iostream>
class Farther
{
public:
virtual void aa()
{
std::cout << "Farther:aa" << std::endl;
}
virtual void bb()
{
std::cout << "Farther:bb" << std::endl;
}
};
class Son : public Farther
{
public:
void aa()
{
std::cout << "Son:aa" << std::endl;
}
void bb()
{
std::cout << "Son:bb" << std::endl;
}
};
int main()
{
Farther *p = new Son;
p->aa();
p->Farther::aa();
return 0;
}
以上代码的比较简陋,主要是为了更加简洁的理解虚函数列表和隐藏的成员虚函数列表指针.
首先,虚函数列表是基于重写实现的,也就是说,当在父类中声明虚函数时,必须在子类中重写
才能实现在主函数中声明的 通过父类类型,new一个子类对象;然后使用对象指针p才能调用
到子类对象的函数aa();
好了,现在开始分析一下虚函数列表的机制.
首先,当在父类中声明一个虚函数,也就是在函数名前加上了virtual关键字,
程序在编译器就会给类添加一个隐藏成员----vfptr,一个二级指针,(为什么说是二级指针呢,因为虚函数
v_table是一个函数指针数组,所以指向这个指针数组的指针便是一个二级指针.) 在增加这个成员的同时,
也增加了一个函数指针列表.
接下来,在主函数中通过父类来new一个子类类型的对象, 这个时候,子类对象也生成一个隐藏的成员,属于子类对象自己的
虚函数指针vfptr_s,以及拷贝了一份父类的函数指针列表,vfptr_s指向的就是拷贝的这份函数指针列表.
这个时候需要注意,子类对象和父类的虚指针和列表的区别在于父类的虚指针和列表在编译器就已经编译完成,
而子类对象的虚指针和列表是在创建对象的时候才产生的.
拷贝过来之后,上面提过,多态的实现是必须基于重写的, 所以此时在子类中的函数必须和父类的函数名和参数列表必须是一样的,
在父类的函数加了virtual关键字之后,其实在子类的重写函数其实也是默认加了关键字virtual的(在类功能多的时候可以加上virtual关键字以区别普通成员函数),
在这个时候,重写的函数会把拷贝过来的列表中对应的函数依次覆盖掉.(这里便是覆盖和重写的区别).
所以通过p->aa(),调用子类对象的aa()函数其实等价于vfptr_s->(*fp)();
如果调用的是bb(),则数组往后索引 ,相当于vfptr_s->(*fp)[1]();
在想调用父类的函数时,也可以通过父类的作用域来进行调用.正如主函数中写的p->Farther::aa();