C++面试经常会被问的问题就是多态原理。如果对C++面向对象本质理解不是特别好,问到这里就会崩。 下面从基本到原理,详细说说多态的实现:虚函数 & 虚函数表。
1. 多态的本质:
形式上,使用统一的父类指针做一般性处理。但是实际执行时,这个指针可能指向子类对象。
形式上,原本调用父类的方法,但是实际上会调用子类的同名方法。
坦白的说,多态就是为了通过使用父类的指针,能够调用父类与子类他们各自的方法。如果不使用多态,用父类指针调用子类的方法时,也会调用到父类的方法。
【注意】
程序执行时,父类指针指向父类对象,或子类对象时,在形式上是无法分辨的。只有通过多态机制,才能执行真正对应的方法。
2. 虚函数:
在父类的方法函数前,增加 virtual 便可以使这个函数变为虚函数,如:
需要注意一点,例子用的是内联函数,封装到外部时,具体方法实现前不用加 virtual,用了会出错。
1 class Father 2 { 3 public: 4 virtual void play() //父类的 play() 方法前增加 virtual 关键字,这个函数便成为了虚函数 5 { 6 std::cout << "这是个父类的play" << std::endl; 7 } 8 }; 9 10 class Son : public Father 11 { 12 public: 13 void play() 14 { 15 std::cout << "这是个子类的Play" << std::endl; 16 } 17 };
3. 虚函数的继承:
如果某个成员函数被声明为虚函数,那么它的子类【派生类】中所继承的成员函数,也会变为虚函数。
如果在子类中重写这个虚函数,可以不用再写 virtual ,但是仍建议写上 virtual,这样会使代码更可读,如13行:
1 class Father 2 { 3 public: 4 virtual void play() //父类的 play() 方法前增加 virtual 关键字,这个函数便为虚函数 5 { 6 std::cout << "这是个父类的play" << std::endl; 7 } 8 }; 9 10 class Son : public Father 11 { 12 public: 13 virtual void play() //派生类继承的虚函数前,可以不加 virtual,但加上会使代码更加可读 14 { 15 std::cout << "这是个子类的Play" << std::endl; 16 } 17 };
4. 虚函数的原理:
虚函数的原理是通过虚函数表来实现的,虚函数表是编译器搞出来的东西他并不存在与对象中,先看下边代码:
1 #include <iostream> 2 using namespace std; 3 4 class Father 5 { 6 public: 7 virtual void func_1() { cout << "Father::func_1" << endl; } 8 virtual void func_2() { cout << "Father::func_2" << endl; } 9 virtual void func_3() { cout << "Father::func_3" << endl; } 10 11 }; 12 13 int main(void) 14 { 15 Father father_1; //虚函数表就保存在这个 father 对象里边 16 17 cout << "sizeof(father_1)=="<< sizeof(father_1) << endl; 18 19 }
运行后打印一下,看看 father 对象占用多大内存空间。
运行结果:sizeof(father_1)==4
3个虚函数为什么只占4个字节?因为他存的是一张表,他没有占用对象的内存空间,对象中只存在一个指针,指向一个虚函数表,如下方示意图,不管你有多少个虚函数,他都在虚函数表里:
===========================================================================================================================