class的实质 class内存分布 class 是一种特殊的 struct
在内存中 class 依旧可以看作变量的集合
class 与 struct 遵循相同的内存对齐规则
class 中的成员函数与成员变量是分开存放的 每个变量都有独立 的成员变量(位于栈空间或堆空间或全局数据区) 所有对象共享 类中的成员函数(位于代码段)
答案都是20个字节。
那么如果我们在class中添加函数呢?
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 #include <iostream> #include <string> using namespace std;#pragma pack(4) class A { int i; int j; char c; double d; public : void print () { cout << "i = " << i << ", " << "j = " << j << ", " << "c = " << c << ", " << "d = " << d << endl; } }; struct B { int i2; int j2; char c2; double d2; }; #pragma pack() int main () { A a; cout << "sizeof(A) = " << sizeof (A) << endl; cout << "sizeof(a) = " << sizeof (a) << endl; cout << "sizeof(B) = " << sizeof (B) << endl; a.print (); B* p = reinterpret_cast <B*>(&a); p->i2 = 1 ; p->j2 = 2 ; p->c2 = 'c' ; p->d2 = 3 ; a.print (); p->i2 = 100 ; p->j2 = 200 ; p->c2 = 'C' ; p->d2 = 3.14 ; a.print (); return 0 ; }
1 2 3 4 5 6 7 8 fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test sizeof (A) = 20 sizeof (a) = 20 sizeof (B) = 20 i = 0 , j = 0 , c = `, d = -2.84627e+237 i = 1 , j = 2 , c = c, d = 3 i = 100 , j = 200 , c = C, d = 3.14
stuct A和class B内存排布相同。因此用指针类型转换后也可以直接访问成员变量。
运行时的对象退化为结构体 的形式
所有成员变量 在内存中依次排布
成员变量间可能存在内存空隙
可以通过内存地址 直接访问成员变量
访问权限关键字在运行时失效
类中的成员函数位于代码段中
调用成员函数时对象地址 作为参数隐式传递
成员函数通过对象地址 访问成员变量
C+ + 语法规则隐藏了对象地址的传递过程
C语言实现面向对象 用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 #include <iostream> #include <string> using namespace std;class Demo { int mi; int mj; public : Demo (int i, int j) { mi = i; mj = j; } int getI () { return mi; } int getJ () { return mj; } int add (int value) { return mi + mj + value; } }; int main () { Demo d (1 , 2 ) ; cout << "sizeof(d) = " << sizeof (d) << endl; cout << "d.getI() = " << d.getI () << endl; cout << "d.getJ() = " << d.getJ () << endl; cout << "d.add(3) = " << d.add (3 ) << endl; return 0 ; }
1 2 3 4 5 fengyun@ubuntu:~/share$ ./test sizeof(d) = 8 d.getI() = 1 d.getJ() = 2 d.add(3) = 6
那我们如果用C语言实现同样的逻辑,那该怎么写?
C语言没有class关键字,不可以在类内写成员函数。C语言写面向对象的代码,必须手动传入对象地址 。C语言中没有private信息隐藏,我们因此定义typedef void Demo;
Demo_Create返回的类型变为void*,我们无法直接通过返回的对象直接访问成员变量。
访问权限关键字在运行时失效,private 和 protected 的访问权限仅在编译时候有效。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 struct ClassDemo { int mi; int mj; }; Demo* Demo_Create (int i, int j) { struct ClassDemo * ret = (struct ClassDemo*)malloc (sizeof (struct ClassDemo)); if ( ret != NULL ) { ret->mi = i; ret->mj = j; } return ret; } int Demo_GetI (Demo* pThis) { struct ClassDemo * obj = (struct ClassDemo*)pThis; return obj->mi; } int Demo_GetJ (Demo* pThis) { struct ClassDemo * obj = (struct ClassDemo*)pThis; return obj->mj; } int Demo_Add (Demo* pThis, int value) { struct ClassDemo * obj = (struct ClassDemo*)pThis; return obj->mi + obj->mj + value; } void Demo_Free (Demo* pThis) { free (pThis); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int main () { Demo* d = Demo_Create(1 , 2 ); printf ("sizeof(*d) = %d" ,sizeof (*d)); printf ("d.mi = %d\n" , Demo_GetI(d)); printf ("d.mj = %d\n" , Demo_GetJ(d)); printf ("Add(3) = %d\n" , Demo_Add(d, 3 )); Demo_Free(d); return 0 ; }
1 2 3 4 fengyun@ubuntu:~/share$ ./test d.mi = 1 d.mj = 2 Add(3) = 6
最终打印效果一致。
小结
C+ + 中的类对象在内存布局上与结构体相同
成员变量和成员函数在内存中分开存放
访问权限关键字在运行时失效
调用成员函数时对象地址作为参数隐式传递
继承对象模型 简单的C++继承对象模型
可以观察到大小是增加了4个字节,即int类型大小。那么内存排布是怎样的呢?
我选择临时定义一个Test结构体,Test结构体内存分布如下。
并且使用强制类型转换,把Derived *指针类型转换为Test *(p指针)
用p指针修改Test对象的成员变量值。改变过后调用Derived类内的成员函数print(),观察到这样竟然也修改Derived定义的结构体的成员变量的值。这说明Test结构体内存分布布局和Derived内存分布布局是相同的。
但是按照protected的成员变量在外界应当是访问不到的啊。这是因为访问权限关键字在运行时失效,在成员函数的内部有了这个隐藏的地址当然可以直接访问对象的成员变量。
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 #include <iostream> #include <string> using namespace std;#progma pack(4) class Demo { protected : int mi; int mj; }; class Derived : public Demo{ int mk; public : Derived (int i, int j, int k) { mi = i; mj = j; mk = k; } void print () { cout << "mi = " << mi << ", " << "mj = " << mj << ", " << "mk = " << mk << endl; } }; struct Test { int mi; int mj; int mk; }; #progma pack() int main () { cout << "sizeof(Demo) = " << sizeof (Demo) << endl; cout << "sizeof(Derived) = " << sizeof (Derived) << endl; Derived d (1 , 2 , 3 ) ; Test* p = reinterpret_cast <Test*>(&d); cout << "Before changing ..." << endl; d.print (); p->mi = 10 ; p->mj = 20 ; p->mk = 30 ; cout << "After changing ..." << endl; d.print (); return 0 ; }
继承而来的内存排布和Test结构体是一样的。
1 2 3 4 5 6 7 fengyun@ubuntu:~/share$ ./test sizeof(Demo) = 8 sizeof(Derived) = 12 Before changing ... mi = 1, mj = 2, mk = 3 After changing ... mi = 10, mj = 20, mk = 30
虚函数的继承对象模型 C+ + 多态的实现原理(相同的行为方式【调用语句】,不同的行为结果)
当类中声明虚函数 时 , 编译器会在类中生成一个虚函数表
虚函数表是一个存储成员函数地址的数据结构
虚函数表是由编译器自动生成与维护的
virtual 成员函数 会被编译器放入虚函数表中
存在虚函数时 ,每个对象中都有一个指向虚函数表的指针
C++的多态就是依靠虚函数
当生成一个类对象的时候,编译器自动为这个对象“塞”一个指针变量,而这个指针指向创建的虚函数表。
具体调用类内的函数时:p->add(v)
具体调用过程:
三次寻址,因此虚函数调用效率低于 普通成员函数。
接着来验证一下带有虚函数的继承对象模型的内存分布:
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 #include <iostream> #include <string> using namespace std;class Demo { protected : int mi; int mj; public : virtual void print () { cout << "mi = " << mi << ", " << "mj = " << mj << endl; } }; class Derived : public Demo{ int mk; public : Derived (int i, int j, int k) { mi = i; mj = j; mk = k; } void print () { cout << "mi = " << mi << ", " << "mj = " << mj << ", " << "mk = " << mk << endl; } }; struct Test { void * p; int mi; int mj; int mk; }; int main () { cout << "sizeof(Demo) = " << sizeof (Demo) << endl; cout << "sizeof(Derived) = " << sizeof (Derived) << endl; Derived d (1 , 2 , 3 ) ; Test* p = reinterpret_cast <Test*>(&d); cout << "Before changing ..." << endl; d.print (); p->mi = 10 ; p->mj = 20 ; p->mk = 30 ; cout << "After changing ..." << endl; d.print (); return 0 ; }
1 2 3 4 5 6 7 fengyun@ubuntu:~/share$ ./test sizeof(Demo) = 16 sizeof(Derived) = 24 Before changing ... mi = 1, mj = 2, mk = 3 After changing ... mi = 10, mj = 20, mk = 30
ubantu64位的情况下,Demo类和Derived类都比之前多了8个字节,即一个指针的字节大小。
并且注意Test结构类型
1 2 3 4 5 6 7 struct Test { void* p; int mi; int mj; int mk; };
说明类的虚函数表指针默认放在类的成员变量之前 。
C语言实现继承与多态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #ifndef _51_2_H_ #define _51_2_H_ typedef void Demo;typedef void Derived;Demo* Demo_Create (int i, int j) ;int Demo_GetI (Demo* pThis) ;int Demo_GetJ (Demo* pThis) ;int Demo_Add (Demo* pThis, int value) ;void Demo_Free (Demo* pThis) ;Derived* Derived_Create (int i, int j, int k) ;int Derived_GetK (Derived* pThis) ;int Derived_Add (Derived* pThis, int value) ;#endif
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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 #include "51-2.h" #include "malloc.h" static int Demo_Virtual_Add (Demo* pThis, int value) ;static int Derived_Virtual_Add (Demo* pThis, int value) ;struct VTable // 2. 定义虚函数表数据结构{ int (*pAdd)(void *, int ); }; struct ClassDemo { struct VTable * vptr ; int mi; int mj; }; struct ClassDerived { struct ClassDemo d ; int mk; }; static struct VTable g_Demo_vtbl = { Demo_Virtual_Add }; static struct VTable g_Derived_vtbl = { Derived_Virtual_Add }; Demo* Demo_Create (int i, int j) { struct ClassDemo * ret = (struct ClassDemo*)malloc (sizeof (struct ClassDemo)); if ( ret != NULL ) { ret->vptr = &g_Demo_vtbl; ret->mi = i; ret->mj = j; } return ret; } int Demo_GetI (Demo* pThis) { struct ClassDemo * obj = (struct ClassDemo*)pThis; return obj->mi; } int Demo_GetJ (Demo* pThis) { struct ClassDemo * obj = (struct ClassDemo*)pThis; return obj->mj; } static int Demo_Virtual_Add (Demo* pThis, int value) { struct ClassDemo * obj = (struct ClassDemo*)pThis; return obj->mi + obj->mj + value; } int Demo_Add (Demo* pThis, int value) { struct ClassDemo * obj = (struct ClassDemo*)pThis; return obj->vptr->pAdd(pThis, value); } void Demo_Free (Demo* pThis) { free (pThis); } Derived* Derived_Create (int i, int j, int k) { struct ClassDerived * ret = (struct ClassDerived*)malloc (sizeof (struct ClassDerived)); if ( ret != NULL ) { ret->d.vptr = &g_Derived_vtbl; ret->d.mi = i; ret->d.mj = j; ret->mk = k; } return ret; } int Derived_GetK (Derived* pThis) { struct ClassDerived * obj = (struct ClassDerived*)pThis; return obj->mk; } static int Derived_Virtual_Add (Demo* pThis, int value) { struct ClassDerived * obj = (struct ClassDerived*)pThis; return obj->mk + value; } int Derived_Add (Derived* pThis, int value) { struct ClassDerived * obj = (struct ClassDerived*)pThis; return obj->d.vptr->pAdd(pThis, value); }
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 #include "stdio.h" #include "51-2.h" void run (Demo* p, int v) { int r = Demo_Add(p, v); printf ("r = %d\n" , r); } int main () { Demo* pb = Demo_Create(1 , 2 ); Derived* pd = Derived_Create(1 , 22 , 333 ); printf ("pb->add(3) = %d\n" , Demo_Add(pb, 3 )); printf ("pd->add(3) = %d\n" , Derived_Add(pd, 3 )); run(pb, 3 ); run(pd, 3 ); Demo_Free(pb); Demo_Free(pd); return 0 ; }
多态 多态的概念 只要是子类对象,我们需要默认调用子类重写过后的自定义的函数 而不是执行父类继承的版本函数
面向对象中期望的行为 - 根据实际的对象类型判断如何调用重写函数 - 父类指针( 引用 ) 指向父类对象则调用父类中定义的函数 • 父类指针( 引用 ) 指向子类对象则调用子类中定义的重写函数
面向对象中的多态的概念
根据实际的对象类型决定 函数调用的具体目标
同样的调用语句 在实际运行时有多种不同的表现形态
C+ + 语言直接支持多态的概念
通过使用 virtual 关键字对多态进行支持
被 virtual 声明的函数被重写后具有多态特性
被 virtual 声明的函数叫做虚函数
由于继承的关系,父类中vitrual关键字也会被子类继承(我们在子类中写不写virtual都无所谓)
多态的意义
在程序运行过程中展现出动态 的特性
函数重写必须多态实现 (virtual),否则没有意义
多态是面向对象组件化程序设计 的基础特性
理论中的概念 静态联编:在程序的编译期间就能确定具体的函数调用–比如:函数重载 动态联编:在程序实际运行后才能确定具体的函数调用–比如:函数重写
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 #include <iostream> #include <string> using namespace std;class Parent { public : virtual void func () { cout << "Parent::void func()" << endl; } virtual void func (int i) { cout << "Parent::void func(int i) : " << i << endl; } virtual void func (int i, int j) { cout << "Parent::void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl; } }; class Child : public Parent{ public : void func (int i, int j) { cout << "Child::void func(int i, int j) : " << i + j << endl; } void func (int i, int j, int k) { cout << "Child::void func(int i, int j, int k) : " << i + j + k << endl; } }; void run (Parent* p) { p->func (1 , 2 ); } int main () { Parent p; p.func (); p.func (1 ); p.func (1 , 2 ); cout << endl; Child c; c.func (1 , 2 ); cout << endl; run (&p); run (&c); return 0 ; }
1 2 3 4 5 6 7 8 9 10 fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test Parent::void func() Parent::void func(int i) : 1 Parent::void func(int i, int j) : (1, 2) Child::void func(int i, int j) : 3 Parent::void func(int i, int j) : (1, 2) Child::void func(int i, int j) : 3
小结
函数重写只可能发生在父类与子类之间
根据实际对象的类型确定调用的具体函数
virtual 关键字是 C+ + 中支持多态的唯一方式
被重写的虚函数可表现出多态的特性
被遗弃的多重继承 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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <iostream> #include <string> using namespace std;class BaseA { int ma; public : BaseA (int a) { ma = a; } int getA () { return ma; } }; class BaseB { int mb; public : BaseB (int b) { mb = b; } int getB () { return mb; } }; class Derived : public BaseA, public BaseB{ int mc; public : Derived (int a, int b, int c) : BaseA (a), BaseB (b) { mc = c; } int getC () { return mc; } void print () { cout << "ma = " << getA () << ", " << "mb = " << getB () << ", " << "mc = " << mc << endl; } }; int main () { cout << "sizeof(Derived) = " << sizeof (Derived) << endl; Derived d (1 , 2 , 3 ) ; d.print (); cout << "d.getA() = " << d.getA () << endl; cout << "d.getB() = " << d.getB () << endl; cout << "d.getC() = " << d.getC () << endl; cout << endl; BaseA* pa = &d; BaseB* pb = &d; cout << "pa->getA() = " << pa->getA () << endl; cout << "pb->getB() = " << pb->getB () << endl; cout << endl; void * paa = pa; void * pbb = pb; if ( paa == pbb ) { cout << "Pointer to the same object!" << endl; } else { cout << "Error" << endl; } cout << "pa = " << pa << endl; cout << "pb = " << pb << endl; cout << "paa = " << paa << endl; cout << "pbb = " << pbb << endl; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fengyun@ubuntu:~/share$ ./test sizeof(Derived) = 12 ma = 1, mb = 2, mc = 3 d.getA() = 1 d.getB() = 2 d.getC() = 3 pa->getA() = 1 pb->getB() = 2 Error pa = 0x7ffd163e869c pb = 0x7ffd163e86a0 paa = 0x7ffd163e869c pbb = 0x7ffd163e86a0
通过多重继承得到的对象可能拥有”不 同 的 地 址 “ !! 解决方案 : 无
多重继承问题二:虚继承 多重继承可能产生冗余的成员
当多重继承关系出现闭合时将产生数据冗余的问题 ! ! ! !
解决方案 : 虚继承
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 #include <iostream> #include <string> using namespace std;class People { string m_name; int m_age; public : People (string name, int age) { m_name = name; m_age = age; } void print () { cout << "Name = " << m_name << ", " << "Age = " << m_age << endl; } }; class Teacher : virtual public People{ public : Teacher (string name, int age) : People (name, age) { } }; class Student : virtual public People{ public : Student (string name, int age) : People (name, age) { } }; class Doctor : public Teacher, public Student{ public : Doctor (string name, int age) : Teacher (name, age), Student (name, age), People (name, age) { } }; int main () { Doctor d ("fengyun" , 20 ) ; d.print (); return 0 ; }
虚继承:
中间层父类不再关心顶层父类的初始化
虚继承能够解决数据冗余问题
最终子类必须直接调用顶层父类的构造函数
问 题 : 当架构设计中需要继承时,无法确定使用直接继承还是虚继承
多重继承问题三:多个虚函数表
需要进行强制类型转换时 , C+ + 中推荐使用新式类型转换关键字 ! ! 解决方案 : dynamic_cast
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 #include <iostream> #include <string> using namespace std;class BaseA { public : virtual void funcA () { cout << "BaseA::funcA()" << endl; } }; class BaseB { public : virtual void funcB () { cout << "BaseB::funcB()" << endl; } }; class Derived : public BaseA, public BaseB{ }; int main () { Derived d; BaseA* pa = &d; BaseB* pb = &d; BaseB* pbe = (BaseB*)pa; BaseB* pbc = dynamic_cast <BaseB*>(pa); cout << "sizeof(d) = " << sizeof (d) << endl; cout << "Using pa to call funcA()..." << endl; pa->funcA (); cout << "Using pb to call funcB()..." << endl; pb->funcB (); cout << "Using pbc to call funcB()..." << endl; pbc->funcB (); cout << endl; cout << "pa = " << pa << endl; cout << "pb = " << pb << endl; cout << "pbe = " << pbe << endl; cout << "pbc = " << pbc << endl; return 0 ; }
工程中的多重继承 单继承某个类 + 实现(多个) 接口
一些有用的工程建议
先继承自一个父类,然后实现多个接口
父类中提供equal()成员函数
equal()成员函数用于判断指针是否指向当前对象
与多重继承相关的强制类型转换用dynamic_cast
完成
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 82 83 84 85 86 87 88 #include <iostream> #include <string> using namespace std;class Base { protected : int mi; public : Base (int i) { mi = i; } int getI () { return mi; } bool equal (Base* obj) { return (this == obj); } }; class Interface1 { public : virtual void add (int i) = 0 ; virtual void minus (int i) = 0 ; }; class Interface2 { public : virtual void multiply (int i) = 0 ; virtual void divide (int i) = 0 ; }; class Derived : public Base, public Interface1, public Interface2{ public : Derived (int i) : Base (i) { } void add (int i) { mi += i; } void minus (int i) { mi -= i; } void multiply (int i) { mi *= i; } void divide (int i) { if ( i != 0 ) { mi /= i; } } }; int main () { Derived d (100 ) ; Derived* p = &d; Interface1* pInt1 = &d; Interface2* pInt2 = &d; cout << "p->getI() = " << p->getI () << endl; pInt1->add (10 ); pInt2->divide (11 ); pInt1->minus (5 ); pInt2->multiply (8 ); cout << "p->getI() = " << p->getI () << endl; cout << endl; cout << "pInt1 == p : " << p->equal (dynamic_cast <Base*>(pInt1)) << endl; cout << "pInt2 == p : " << p->equal (dynamic_cast <Base*>(pInt2)) << endl; return 0 ; }
关于虚函数的问题 构造函数是否可以成为虚函数 ? 析构函数是否可以成为虚函数 ?
构造函数不可能成为虚函数 在构造函数执行结束后 , 虚函数表指针才会被正确的初始化
析构函数可以成为虚函数 建议在设计类时将析构函数声明为虚函数
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 #include <iostream> #include <string> using namespace std;class Base { public : Base () { cout << "Base()" << endl; func (); } virtual void func () { cout << "Base::func()" << endl; } virtual ~Base () { func (); cout << "~Base()" << endl; } }; class Derived : public Base{ public : Derived () { cout << "Derived()" << endl; func (); } virtual void func () { cout << "Derived::func()" << endl; } ~Derived () { func (); cout << "~Derived()" << endl; } }; int main () { Base* p = new Derived (); delete p; return 0 ; }
如果不将析构函数设为虚函数,p指针类型是Base*,delete p将会执行Base的析构函数
构造函数中是否可以发生多态? 析构函数中是否可以发生多态?
构造函数中不可能发生多态行为,只调用当前类中的版本 在构造函数执行时 , 虚函数表指针未被正确初始化
析构函数中不可能发生多态行为,只调用当前类中的版本 在析构函数执行时 , 虚函数表指针已经被销毁