十个例子让你完全搞懂 C++ 的虚函数(不懂来找我)

Author: bakari Date: 2012.4.8

虚函数是C++中非常重要的一个概念,它最大的好处是能够触发动态绑定。C++中的函数默认不使用动态绑定,要触发动态绑定,必须满足 两个条件:

第一,只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定;

第二,必须通过基类类型的指针或引用进行函数的调用。具体理解指针或引用在使用继承层次中某一类型的对象时会发生什么,本文不展开讨论,

这两天主要研习了虚函数的具体应用这一块,而它的应用又非常广泛,学MFC的应该能够感受到它的强大,要说是总结也不一定能够总结全,本人目前也处在studying中,所以用10个具体的例子来说明。例子是从难 到易,看到的朋友如果懂前面的可以不用看后面的。每一个例子就是一个类,通过类在内存中的布局来形象地分析虚函数究竟是如何运作的。图表示可能抽象一点,一般带有V开头的表示一个虚函数表,如果是学过编译原理这门课就很容易看懂,没学过的只要懂虚函数的一些机制,耐着性子也是没问题的。每个图示都配有相应的代码。可以对照着代码来看。

1、 虚函数继承的复杂例子

2、 菱形继承无虚拟继承的情况

3、 虚拟继承的简单情况

4、 单一普通继承(无虚函数)

5、 单一继承(含虚函数)(虚函数表只有一个)

6、 多重继承(不含虚函数)

7、 多重继承(一个含虚函数,一个不含虚函数)

8、 多重继承(两个都含有虚函数)

9、 纯虚汗继承

10、 private 的虚函数

1、虚函数继承的复杂例子,见下图:

见图:左边是这个类的内存布局,右边是继承图示。 farther类和Uncle类都是虚拟继承,其内部也都有偏移表,但我觉得这两个表只是内部隐藏的,不在Son的内存布局中表示出来,本题Son的内存只有32个字节,如果表示出来就不止32个了,但是下面这个地方在内存中显示是00 00 00 00,我猜想是不是GrandFather的偏移地址。

VbtSon(Father)

Farther~Num

VbtSon(Uncle)

Uncle~Num

Son~Num

Offset(这个地方??)

Vftable(GrandFather)

GrandFather~Num

例子代码:

代码语言:javascript复制 1 class GrandFather

2 {

3 public:

4 GrandFather():i_G(5){cout<<"GrandFather() is called!"<

5 virtual ~GrandFather(){cout<<"~GrandFather() is called!"<

6 public:

7 virtual void Test(){cout<<"GrandFather::Test() is called!"<

8 private:

9 int i_G;

10 };

11

12 class Father: virtual public GrandFather //虚拟继承

13 {

14 public:

15 Father():i_F(7){cout<<"Father() is called!"<

16 virtual ~Father(){cout<<"~Father() is called!"<

17 public:

18 virtual void Test(){cout<<"Father::Test() is called!"<

19 private:

20 int i_F;

21 };

22

23 class Uncle: virtual public GrandFather //虚拟继承

24 {

25 public:

26 Uncle():i_U(3){cout<<"Uncle is called!"<

27 virtual ~Uncle(){cout<<"~Uncle is called!"<

28 public:

29 virtual void Test(){cout<<"Uncle ::Test() is called!"<

30 private:

31 int i_U;

32 };

33

34 class Son:public Father,public Uncle

35 {

36 public:

37 Son():i_S(9){cout<<"Son is called!"<

38 virtual ~Son(){cout<<"~Son is called!"<

39 public:

40 virtual void Test(){cout<<"Son ::Test() is called!"<

41 private:

42 int i_S;

43 };

44

45 int main(void)

46 {

47 Son p;

48 p.Test();

49 cout<

50 cout<

51 cout<

52 return 0;

53 }运行情况:

2、 菱形继承无虚拟继承的情况

VPTr1(Father)

GrandFarther~Num

Father~Num

VPtr(Uncle)

GrandFarther~Num

Uncle~Num

Son~Num

代码语言:javascript复制 1 #include

2 using namespace std;

3 class GrandFather

4 {

5 public:

6 GrandFather():i_G(5){cout<<"GrandFather() is called!"<

7 virtual ~GrandFather(){cout<<"~GrandFather() is called!"<

8 public:

9 virtual void Test(){cout<<"GrandFather::Test() is called!"<

10 private:

11 int i_G;

12 };

13 class Father: public GrandFather //无虚拟继承

14 {

15 public:

16 Father():i_F(7){cout<<"Father() is called!"<

17 virtual ~Father(){cout<<"~Father() is called!"<

18 public:

19 virtual void Test(){cout<<"Father::Test() is called!"<

20 private:

21 int i_F;

22 };

23

24 class Uncle: public GrandFather //无虚拟继承

25 {

26 public:

27 Uncle():i_U(3){cout<<"Uncle is called!"<

28 virtual ~Uncle(){cout<<"~Uncle is called!"<

29 public:

30 virtual void Test(){cout<<"Uncle ::Test() is called!"<

31 private:

32 int i_U;

33 };

34

35 class Son:public Father,public Uncle

36 {

37 public:

38 Son():i_S(9){cout<<"Son is called!"<

39 virtual ~Son(){cout<<"~Son is called!"<

40 public:

41 virtual void Test(){cout<<"Son ::Test() is called!"<

42 private:

43 int i_S;

44 };

45

46 int main(void)

47 {

48 Son p;

49 p.Test();

50 cout<

51 cout<

52 cout<

53

54 return 0;

55 }运行情况:

3、 虚拟继承的简单情况 见下图:

VPTrD(A) 4

Offset(A) 4

A~number 4

D~number 4

VPtr(Base) 4

Base~Number

12 + 3cc + 4 = 40

代码语言:javascript复制 1 class Base

2 {

3 public:

4 Base(){strcpy_s(ch_rc,"abcdefg");} //初始化Base()::im

5 public:

6 virtual void Read(){cout<<"Base::Read()is called!"<

7 private:

8 char ch_rc[12];

9 bool ir;

10 int im;

11 };

12 class A: virtual public Base //虚拟继承

13 {

14 public:

15 A():im_A(5){} //初始化A()::im_A

16 public:

17 virtual void Read(){cout<<"A::Read()is called!"<

18 private:

19 int im_A;

20 };

21 class D:public A

22 {

23 public:

24 D():im_D(3){}

25 public:

26 virtual void Read(){cout<<"D::Read()is called!"<

27 private:

28 int im_D;

29 };

30 int _tmain(int argc, _TCHAR* argv[])

31 {

32 D obj;

33 cout<

34 return 0;

35 }运行情况:

4、单一普通继承(无虚函数)(这个没什么好说的)

内存布局

Father~Number

Son~Number

代码语言:javascript复制 1 class Father

2 {

3 public:

4 Father(){cout<<"Father() is called!"<

5 void TestF(const int &m){

6 i_B=m;

7 cout<<"Father::TestF() is called!"<

8 }

9 void Test(){cout<<"Base::Test() is called!"<

10 ~Father(){cout<<"~Father() is called!"<

11 private:

12 int i_B;

13 };

14

15 class Son:public Father

16 {

17 public:

18 Son():i_A(5){cout<<"Son() is called!"<

19 void Test(){cout<<"Son::Test() is called!"<

20 ~Son(){cout<<"~Son() is called!"<

21 private:

22 int i_A;

23 };

24 int main(int argc,char *argv[])

25 {

26 Father *p=new Son;

27 //Father *p=NULL;

28 p->Test();

29 delete p;

30 p=NULL;

31 cout<

32 return 0;

33 }5、单一继承(含虚函数)(虚函数表只有一个)见图:

VPTr(father)

Father~number

Son~number

Child~number

代码语言:javascript复制 1 #include

2 using namespace std;

3 class Father

4 {

5 public:

6 Father(){cout<<"Father() is called!"<

7 virtual void Test(){cout<<"Base::Test() is called!"<

8 virtual ~Father(){cout<<"~Father() is called!"<

9 private:

10 int i_B;

11 };

12

13 class Son:public Father

14 {

15 public:

16 Son():i_A(5){cout<<"Son() is called!"<

17 void Test(){cout<<"Son::Test() is called!"<

18 ~Son(){cout<<"~Son() is called!"<

19 private:

20 int i_A;

21 };

22 int main(int argc,char *argv[])

23 {

24 Father *p=new Son;

25 //Father *p=NULL;

26 p->Test();

27 delete p;

28 p=NULL;

29 cout<

30 return 0;

31 }运行情况:

6、多重继承(不含虚函数)(这个也没什么好说的)

直接看代码:

代码语言:javascript复制 1 #include

2 using namespace std;

3 class Father

4 {

5 public:

6 Father():i_B(6){cout<<"Father() is called!"<

7 void TestF(const int &m){

8 i_B=m;

9 cout<<"Father::TestF() is called!"<

10 }

11 void Test(){cout<<"Father::Test() is called!"<

12 ~Father(){cout<<"~Father() is called!"<

13 private:

14 int i_B;

15 };

16 class Son

17 {

18 public:

19 Son():i_A(5){cout<<"Son() is called!"<

20 void Test(){cout<<"Son::Test() is called!"<

21 ~Son(){cout<<"~Son() is called!"<

22 private:

23 int i_A;

24 };

25

26 class Child:public Father,public Son //多重继承

27 {

28 public:

29 Child():i_C(5){cout<<"Child() is called!"<

30 void Test(){cout<<"Child::Test() is called!"<

31 ~Child(){cout<<"~Child() is called!"<

32 private:

33 int i_C;

34 };

35 int main(int argc,char *argv[])

36 {

37 Father *p=new Child;

38 //Father *p=NULL;

39 p->Test();

40 cout<

41 cout<

42 return 0;

43 }7、多重继承(一个含虚函数,一个不含虚函数)(类似单一继承)

VPTr(father)

Father~number

Son~number

Child~number

代码语言:javascript复制 1 #include

2 using namespace std;

3

4 class Father

5 {

6 public:

7 Father():i_B(6){cout<<"Father() is called!"<

8 virtual void TestF(const int &m){

9 i_B=m;

10 cout<<"Father::TestF() is called!"<

11 }

12 virtual void Test(){cout<<"Father::Test() is called!"<

13 virtual ~Father(){cout<<"~Father() is called!"<

14 private:

15 int i_B;

16 };

17

18 class Son

19 {

20 public:

21 Son():i_A(5){cout<<"Son() is called!"<

22 void Test(){cout<<"Son::Test() is called!"<

23 ~Son(){cout<<"~Son() is called!"<

24 private:

25 int i_A;

26 };

27

28 class Child:public Father,public Son

29 {

30 public:

31 Child():i_C(5){cout<<"Child() is called!"<

32 void Test(){cout<<"Child::Test() is called!"<

33 ~Child(){cout<<"~Child() is called!"<

34 private:

35 int i_C;

36 };

37 int main(int argc,char *argv[])

38 {

39 Father *p=new Child;

40 //Father *p=NULL;

41 p->Test();

42 cout<

43 cout<

44 return 0;

45 }运行情况:

8、多重继承(两个都含有虚函数)

VPTr(father)

Father~number

VPTr(Son)

Son~number Child~number

20个字节

代码语言:javascript复制 1 #include

2 using namespace std;

3

4 class Father

5 {

6 public:

7 Father():i_B(6){cout<<"Father() is called!"<

8 virtual void TestF(const int &m){

9 i_B=m;

10 cout<<"Father::TestF() is called!"<

11 }

12 virtual void Test(){cout<<"Father::Test() is called!"<

13 virtual ~Father(){cout<<"~Father() is called!"<

14 private:

15 int i_B;

16 };

17 class Son

18 {

19 public:

20 Son():i_A(5){cout<<"Son() is called!"<

21 virtual void Test(){cout<<"Son::Test() is called!"<

22 virtual ~Son(){cout<<"~Son() is called!"<

23 private:

24 int i_A;

25 };

26

27 class Child:public Father,public Son

28 {

29 public:

30 Child():i_C(7){cout<<"Child() is called!"<

31 void Test(){cout<<"Child::Test() is called!"<

32 ~Child(){cout<<"~Child() is called!"<

33 private:

34 int i_C;

35 };

36 int main(int argc,char *argv[])

37 {

38 //Father *p=new Child;

39 Child p;

40 //Father *p=NULL;

41 p.Test();

42 cout<

43 cout<

44 return 0;

45 }运行情况:

9、纯虚汗继承

(只在父类中申明,并在子类中实现申明的函数才可以用)

内存分配与前面只含虚函数的情况类似

代码语言:javascript复制 1 #include

2 using namespace std;

3 class A

4 {

5 public:

6 A():i_A(5){cout<<"A() is called!"<

7 public:

8 virtual void Test()= 0; //prue virtual function

9 virtual void Base() {cout<<"this is farther class"<

10 private:

11 int i_A;

12 };

13

14 class B:public A

15 {

16 public:

17 B():i_B(9){}

18 public:

19 void Test() { cout<<" this is SubVirtual!"<

20 void Base() {

21 cout<<"this is subclass Base"<

22 }

23 private:

24 int i_B;

25 };

26

27 int main(void)

28 {

29 A* p = new B; //multstate pointer

30 p->Test();

31 p->Base();

32 cout<

33 return 0 ;

34 }10、private 的虚函数

代码语言:javascript复制 1 #include

2 using namespace std;

3

4 class A

5 {

6 public:

7 virtual void Test() { func();}

8 private:

9 int i_A;

10 virtual void func() {cout<<"A::func () is Called!"<

11 };

12 class B: public A

13 {

14 private:

15 //虽然func()在A类中是private的,但是仍然可以出现在派生类中,并仍然可以与public或者protected的虚函数一样产生多态的效果。

16 virtual void func() { cout<<"B::func() is Called!"<

17 int i_B;

18 };

19

20 int main(void)

21 {

22 //A *p=new B;

23 A p;

24 //B p;

25 //p->func();

26 p.Test();

27 cout<

28 return 0;

29 }运行情况:

OK,做这个东西很辛苦,没办法,搞技术的就得这样。

写这些程序然后进行测试是很轻松的一件事,然而要把这些东西以文字的方式整理出来,的确不是一件容易的事。所以,就有很多IT高手,技术流,Coding很厉害,但对于文字的东西就不是很感冒,不过没关系,每个人有每个人的不同的需求,我是喜欢没事总喜欢写写的人,我想写的越多思路就不会堵塞

祝愿每一个朋友学习愉快,技术成精


TOP