• 一个对象的静态类型在编译的时候就确定了。
  • 一个对象的动态类型是指它指向的对象或则它绑定的对象(因此只有指针和引用才有动态类型),一个对象的动态类型只有运行的时候才会确定。
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 }

 

posted on 2023-08-28 10:07  小凉拖  阅读(25)  评论(0编辑  收藏  举报