- 一个对象的静态类型在编译的时候就确定了。
- 一个对象的动态类型是指它指向的对象或则它绑定的对象(因此只有指针和引用才有动态类型),一个对象的动态类型只有运行的时候才会确定。
1 Quote* p = new b_Quote; // Quote 是基类,b_Quote 是子类
指针 p 的静态类型是 Quote,在编译时已经确定了,但它的动态类型是 b_Quote,运行时才知道
1 class Base { 2 public: 3 Base() { 4 5 } 6 virtual void fun() { 7 cout << "Base function" << endl; 8 } 9 void func() { 10 cout << "normal function" << endl; 11 } 12 }; 13 class Son :public Base { 14 public: 15 Son() { 16 17 } 18 void fun() { 19 cout << "Son function" << endl; 20 } 21 void func() { 22 cout << "Son's normal function" << endl; 23 } 24 }; 25 26 int main() { 27 Base* base = new Son; 28 base->fun(); 29 Base* base1 = new Son; 30 base1->func(); 31 }
编译时编译器会首先确定base的静态类型,然后在静态类型(基类)中查找是否有fun()函数,如果没有这个函数则编译器会报错。如果找到了就检查base是否是指针或则引用,是否是向上转型(父类指针或引用指向子类),fun()是否是虚函数。如果上述条件有一项不满足则该函数的类型就是静态类型,此时确定fun()是静态类型的函数;如果上述都满足那么等到运行时判断它的动态类型,运行时判断它的动态类型后通过动态类型的虚函数指针(在类的前4个字节中,虚函数是属于类的而不是哪个对象的)去找到其所指向的虚函数表,然后在虚函数表中找到该虚函数的地址,并进行调用(虚函数表是一个一维数组,查找虚函数表的时间复杂度是O(1))
fun()是虚函数,base2也是指针,因此需要在运行时才能确定fun()是属于哪个类型的,结果在运行时发现是一个空指针,空指针调用函数会引发异常。func()不是虚函数,编译时确定它是静态类型的函数,因此调用静态类型的函数不会报错。
另外值得注意的是子类最好不要重写父类的非虚函数,如果父类的虚函数有默认参数,子类重写的虚函数有另外一个默认参数,那么多态时使用的是父类的默认参数。
1 class Base { 2 public: 3 Base() { 4 5 } 6 virtual void fun(int a=1) { 7 cout << "Base function" <<" "<<a << endl; 8 } 9 void func() { 10 cout << "normal function" << endl; 11 } 12 }; 13 class Son :public Base { 14 public: 15 Son() { 16 17 } 18 void fun(int a=10) { 19 cout << "Son function" <<" "<<a << endl; 20 } 21 22 }; 23 24 int main() { 25 Base* base = new Son; 26 base->fun(); 27 28 }