静态多态
静态多态通过重载实现多态
重载函数
函数同名,参数不同类型
1 2 3 4 5 6
| void Func1(int a) { cout << "This is Func1(int), Parameter = "<< a << endl; } void Func1(float a) { cout << "This is Func1(float), Parameter = " << a << endl; }
|
重载运算符
1 2 3
| friend ostream& operator << (ostream o,const A& a) { return o<<a.a; }
|
动态多态
动态多态通过虚函数实现多态。使用动态多态必须通过指针或引用才能调用函数。
虚函数
虚函数的声明和调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class A { public: char a; A():a('A'){ cout<<"Create A"<<endl; }
virtual void vFunc1(int a) { cout << "This is A vFunc1(int), Parameter = "<< a << endl; } };
class C : public A { public: C():A(){cout<<"Create C"<<endl;}
virtual void vFunc1(int a) override { cout << "This is C vFunc1(int), Parameter = " << a << endl; } ~C(){ } };
int main(int argc, char* argv[]) { C* c = new C(); A* a = c; c->vFunc1(20);
return 0; }
|
输出:
1
| This is C vFunc1(int), Parameter = 20
|
构造函数与虚析构函数
- 构造函数不能被声明为虚函数。调用虚函数需要借助虚函数指针,虽然虚函数表的建立是在编译阶段,但在实例化后才能知道虚函数指针的具体地址,而父类的构造函数在实例化子类后调用,子类需要知道父类的虚函数指针。如果声明为虚的,则子类不知道父类的虚函数指针,无法调用虚函数。
- 析构函数可以被声明为虚函数。常见于释放父类的成员指针等。
使用父类指针指向子类对象,在释放指针时,不会调用子类的构造函数,造成子类的成员不能释放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class A { public: char* a; A(){ a = new char('A'); cout<<"Create A"<<endl; }
virtual void vFunc1(int a) { cout << "This is A vFunc1(int), Parameter = "<< a << endl; }
~A() { delete a; cout << "Destroy A" << endl; } };
class C : public A { public: C():A(){cout<<"Create C"<<endl;}
virtual void vFunc1(int a) override { cout << "This is C vFunc1(int), Parameter = " << a << endl; }
~C(){ cout << "Destroy C" << endl; } };
int main(int argc, char* argv[]) { A* c_ptr = new C(); delete c_ptr; return 0; }
|
输出:
1 2 3
| Create A Create C Destroy A
|
将A的析构函数声明为虚函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class A { public: char* a; A(){ a = new char('A'); cout<<"Create A"<<endl; }
virtual void vFunc1(int a) { cout << "This is A vFunc1(int), Parameter = "<< a << endl; }
virtual ~A() { delete a; cout << "Destroy A" << endl; } };
class C : public A { public: C():A(){cout<<"Create C"<<endl;}
virtual void vFunc1(int a) override { cout << "This is C vFunc1(int), Parameter = " << a << endl; }
virtual ~C() override { cout << "Destroy C" << endl; } };
int main(int argc, char* argv[]) { A* c_ptr = new C(); delete c_ptr; return 0; }
|
纯虚函数与抽象类
纯虚函数相当于C#中接口中的方法,其子类必须实现该方法,否则不能通过编译。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class A { public: char a; A():a('A'){ cout<<"Create A"<<endl; }
virtual void vFunc1(int a) = 0; }; class C : public A { public: C():A(){cout<<"Create C"<<endl;}
~C(){ } };
int main(int argc, char* argv[]) { C* c = new C();
return 0; }
|
更改后:
1 2 3 4 5 6 7 8 9 10 11 12
| class C : public A { public: C():A(){cout<<"Create C"<<endl;}
virtual void vFunc1(int a) { cout << "Pure Virtual Function of C" << endl; }
~C(){ } };
|
抽象类使用abstract关键字,使用该关键字就不能实例化的类。通常将一些包含纯虚函数的类加此标识以明确区分。
虚函数的底层原理
单继承
虚函数通过虚函数表和虚函数指针实现。
虚函数表、虚函数表指针的生成
结论:
- 编译阶段,生成虚函数表并为生成虚函数表指针做准备。
- 但虚函数表、虚函数表指针、虚函数指针的具体地址是在程序运行时生成。
- 虚函数表是静态的,每个声明了虚函数的类中只有一套虚函数表及虚函数表指针。
只要在一个类中声明了一个以上的虚函数,编译器就会在编译期间为该类生成一个虚函数表。这个虚函数表是静态的,实例化多个对象可以看到每个对象中的虚函数表指针、虚函数表及虚函数指针的地址都是相同的。
编译器如何在编译期间为该类生成虚函数表呢?
具体做法编译器在编译期间在构造函数中插入为虚函数表指针赋值的语句。但具体的指针地址是在运行时动态生成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class B { public: B(){cout<<"Create B"<<endl;}
~B() { cout << "Destroy B" << endl; } };
int main(int argc, char* argv[]) { cout<<"sizeof(B): "<<sizeof(B); return 0; }
|
输出:
在B类中声明虚函数后,实例化一个对象,并进行调试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class B { public: B(){cout<<"Create B"<<endl;}
virtual void Func1() { cout << "B: Func1()" << endl; } virtual void Func2() { cout << "B: Func2()" << endl; } virtual void Func3() { cout << "B: Func3()" << endl; }
~B() { cout << "Destroy B" << endl; } };
int main() { B b; cout<<"sizeof(B): "<<sizeof(B)<<endl; return 0; }
|
输出:
1 2 3
| Create B sizeof(B): 8 Destroy B
|
调试信息:
1 2 3 4 5
| - b {...} B - __vfptr 0x00007ff7cd0bbed8 {***.exe!void(* B::`vftable'[4])()} {0x00007ff7cd0b159b {***.exe!B::Func1(void)}, ...} void * * [0] 0x00007ff7cd0b159b {***.exe!B::Func1(void)} void * [1] 0x00007ff7cd0b1596 {***.exe!B::Func2(void)} void * [2] 0x00007ff7cd0b1591 {***.exe!B::Func3(void)} void *
|
输出显示B类占用内存大小8个字节,这是__vfptr指针的内存大小。
__vfptr指向一个虚函数表,故称__vfptr为虚函数表指针,其中存放着所有虚函数的指针。
继承关系中虚函数是如何运作的。
结论:
- 父类指向(引用)了子类的对象时,子类复制父类虚函数表中的所有函数,构成子类的新虚函数表,同时分配一个指针指向该表。
- 子类覆写了父类的虚函数会覆盖掉父类对应的虚函数指针,而没有覆写父类的虚函数则指向父类的虚函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| class B { public: int* number; B(int n):number(&n){cout<<"Create B"<<endl;}
virtual void Func1() { cout << "B: Func1()" << endl; } virtual void Func2() { cout << "B: Func2()" << endl; } void Func3() { cout << "B: Func3()" << endl; }
virtual ~B() { delete number; cout << "Destroy B" << endl; } };
class C : public B { public: C(int n):B(n){cout<<"Create C"<<endl;}
virtual void Func1() { cout << "This is C Func1()" << endl; }
void CFunc() { cout << "This is CFunc()" << endl; }
virtual ~C() { cout << "Destroy C" << endl; } };
int main() { B* b1 = new B(0); B* b2 = new B(10);
B* c = new C(20); C* c2 = new C(4);
b1->Func1(); b2->Func1(); c->Func1(); c2->Func1(); return 0; }
|
调试信息:
1 2 3 4 5 6 7
| - b1 0x0000021239979d20 {number=0x000000fa490ffb18 {20} } B * - __vfptr 0x00007ff77d2bbed8 {***.exe!void(* B::`vftable'[4])()} {0x00007ff77d2b159b {{***..exe!B::Func1(void)}, ...} void * * [0] 0x00007ff77d2b159b {{***..exe!B::Func1(void)} void * [1] 0x00007ff77d2b1596 {{***..exe!B::Func2(void)} void * [2] 0x00007ff77d2b15af {{***..exe!B::`vector deleting destructor'(unsigned int)} void * + number 0x000000fa490ffb18 {20} int *
|
1 2 3 4 5 6 7
| - b2 0x00000212399796e0 {number=0x000000fa490ffb18 {20} } B * - __vfptr 0x00007ff77d2bbed8 {{***..exe!void(* B::`vftable'[4])()} {0x00007ff77d2b159b {{***..exe!B::Func1(void)}, ...} void * * [0] 0x00007ff77d2b159b {{***..exe!B::Func1(void)} void * [1] 0x00007ff77d2b1596 {{***..exe!B::Func2(void)} void * [2] 0x00007ff77d2b15af {{***..exe!B::`vector deleting destructor'(unsigned int)} void * + number 0x000000fa490ffb18 {20} int *
|
1 2 3 4 5 6 7 8 9
| - c 0x0000021239979140 {...} B * {C} - [C] {...} C + B {number=0x000000fa490ffa18 {2099975544} } B - __vfptr 0x00007ff77d2bc028 {{***..exe!void(* C::`vftable'[4])()} {0x00007ff77d2b15b9 {{***..exe!C::Func1(void)}, ...} void * * [0] 0x00007ff77d2b15b9 {{***..exe!C::Func1(void)} void * //覆写了B的虚函数,一个新的虚函数地址。 [1] 0x00007ff77d2b1596 {{***..exe!B::Func2(void)} void * //没有覆写B的虚函数,和b1、b2的Func2()地址相同。 [2] 0x00007ff77d2b15d2 {{***..exe!C::`vector deleting destructor'(unsigned int)} void * + number 0x000000fa490ffa18 {2099975544} int *
|
1 2 3 4 5
| - c2 0x0000015be6f89610 {...} C * - B {number=0x00000047dd93f718 {-203680392} } B + __vfptr 0x00007ff6f3dcc028 {***.exe!void(* C::`vftable'[4])()} {0x00007ff6f3dc15b9 {***.exe!C::Func1(void)}, ...} void * * + number 0x00000047dd93f718 {-203680392} int *
|
通过比较上面__vfptr中的虚函数指针地址,可以看到虽然c是B类的指针,但虚函数表指针已经是C类的指针了。子类覆写了父类虚函数的,会有一个新的虚函数地址。而没有覆写父类虚函数的,则指向父类的虚函数。
多重继承
多重的单一继承同样符合上文的规则。依次类推即可
多继承
结论:
- 一个继承多个父类的子类会持有所有父类的虚函数表指针。
- 子类覆写了父类中同名的虚函数,则每个父类中的每个同名虚函数均会被覆写。
- 子类覆写了父类的虚函数,则覆盖对应的父类虚函数地址。否则,复制父类虚函数地址。
- 子类中新添加的虚函数会添加到最先继承的父类的虚函数表中。
实验:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| class A { public: char* a; A(){ a = new char('A'); cout<<"Create A"<<endl; }
void AFunc() { cout << "This is AFunc()" << endl; }
virtual void AvFunc() { cout << "This is AvFunc()" << endl; }
virtual void ABvFunc() { cout << "This is ABvFunc() in A" << endl; }
~A() { delete a; cout << "Destroy A" << endl; } };
class B { public: int* number; B(int n):number(new int(n)){cout<<"Create B"<<endl;}
void BFunc() { cout << "BFunc()" << endl; } virtual void BvFunc() { cout << "BvFunc()" << endl; } virtual void ABvFunc() { cout << "This is ABvFunc() in B" << endl; }
virtual ~B() { delete number; cout << "Destroy B" << endl; } };
class C : public A,public B { public: C(int n):B(n){cout<<"Create C"<<endl;}
virtual void ABvFunc() { cout << "This is ABvFunc() in C" << endl; }
void CFunc() { cout << "This is CFunc()" << endl; }
virtual void MyVirtualFunc() { cout << "This is MyVirtualFunc()" << endl; }
virtual ~C() { cout << "Destroy C" << endl; } };
int main() { A* c1 = new C(2); c1->ABvFunc();
C* c2 = new C(3); c2->MyVirtualFunc(); return 0; }
|
调试信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| - c1 0x000001c7ad0f7c00 {...} A * {C} - [C] {...} C
- A {a=0x000001c7ad0ebc30 "A葺葳馫," } A - __vfptr 0x00007ff7c5c7c038 {OOPLearning.exe!void(* C::`vftable'[4])()} {0x00007ff7c5c71569 {OOPLearning.exe!A::AvFunc(void)}, ...} void * * [0] 0x00007ff7c5c71569 {OOPLearning.exe!A::AvFunc(void)} void * [1] 0x00007ff7c5c712da {OOPLearning.exe!C::ABvFunc(void)} void * + a 0x000001c7ad0ebc30 "A葺葳馫," char * //B - B {number=0x000001c7ad0ebab0 {2} } B - __vfptr 0x00007ff7c5c7bd00 {OOPLearning.exe!void(* C::`vftable'[4])()} {0x00007ff7c5c71555 {OOPLearning.exe!B::BvFunc(void)}, ...} void * * [0] 0x00007ff7c5c71555 {OOPLearning.exe!B::BvFunc(void)} void * [1] 0x00007ff7c5c71550 {OOPLearning.exe![thunk]:C::ABvFunc`adjustor{16}' (void)} void * [2] 0x00007ff7c5c715be {OOPLearning.exe![thunk]:C::`vector deleting destructor'`adjustor{16}' (unsigned int)} void * + number 0x000001c7ad0ebab0 {2} int * + __vfptr 0x00007ff7c5c7c038 {OOPLearning.exe!void(* C::`vftable'[4])()} {0x00007ff7c5c71569 {OOPLearning.exe!A::AvFunc(void)}, ...} void * * + a 0x000001c7ad0ebc30 "A葺葳馫," char *
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| - c2 0x000001c7ad0f7c60 {...} C *
- A {a=0x000001c7ad0ebbf0 <字符串中的字符无效。> } A - __vfptr 0x00007ff7c5c7c038 {OOPLearning.exe!void(* C::`vftable'[4])()} {0x00007ff7c5c71569 {OOPLearning.exe!A::AvFunc(void)}, ...} void * * [0] 0x00007ff7c5c71569 {OOPLearning.exe!A::AvFunc(void)} void * [1] 0x00007ff7c5c712da {OOPLearning.exe!C::ABvFunc(void)} void * + a 0x000001c7ad0ebbf0 <字符串中的字符无效。> char * //B - B {number=0x000001c7ad0ebff0 {3} } B - __vfptr 0x00007ff7c5c7bd00 {OOPLearning.exe!void(* C::`vftable'[4])()} {0x00007ff7c5c71555 {OOPLearning.exe!B::BvFunc(void)}, ...} void * * [0] 0x00007ff7c5c71555 {OOPLearning.exe!B::BvFunc(void)} void * [1] 0x00007ff7c5c71550 {OOPLearning.exe![thunk]:C::ABvFunc`adjustor{16}' (void)} void * [2] 0x00007ff7c5c715be {OOPLearning.exe![thunk]:C::`vector deleting destructor'`adjustor{16}' (unsigned int)} void * + number 0x000001c7ad0ebff0 {3} int *
|