c++中的 虚函数 纯虚函数 虚基类

来源:互联网 发布:淘宝天天看直播间 编辑:程序博客网 时间:2024/06/04 18:11

原文出自http://blog.csdn.net/dardgen/article/details/18790977

虚函数 ,纯虚函数, 虚基类,它们都和virtual有关,这三个带有虚字的定义容易使人混淆,下面先从作用上来解释这三个定义的区别:

1.虚函数是用于多态中virtual修饰父类函数,确保父类指针调用子类对象时,运行子类函数的。

2.纯虚函数是用来定义接口的,也就是基类中定义一个纯虚函数,基类不用实现,让子类来实现。

3.虚基类是用来在多继承中,如果父类继承自同一个父类,就只实例化一个父类(说的有点绕,就是只实例化一个爷爷的意思=。=)。

如果上面的没看懂没关系,下面来慢慢解释清楚。


一.虚函数

这个和多态有关,多态的定义不清楚的话到其他地方先了解一下,多态的三个必要条件:1.继承 2.重载 3.父类指针指向子类对象。

下面看程序:

第一个是没有使用virtual的,没有使用多态的:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class A  
  2. {  
  3. public:  
  4.     void printf(){  
  5.         cout<<"printf A"<<endl;  
  6.     }  
  7. };  
  8. class B : public A  
  9. {  
  10. public:  
  11.     void printf(){  
  12.         cout<<"printf B"<<endl;  
  13.     }  
  14. };  
  15. int main(int argc, const char * argv[])  
  16. {  
  17.     A *a = new A();  
  18.     a->printf();  
  19.     B *b = new B();  
  20.     b->printf();  
  21.     return 0;  
  22. }  

结果:

printf A

printf B


这是最基本的用法,没有多态,只有继承,下面是使用了多态但是没有引用virtual关键字的情况,多态的作用请参考其他地方的文章:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int main(int argc, const char * argv[])  
  2. {  
  3.     A *a = new B();  
  4.     a->printf();  
  5.     return 0;  
  6. }  

结果:

printf A

因为类的定义都一样,所以就没再写出来了,当父类指针指向子类对象的时候,如果不使用virtual,父类调用方法的时候还是调用了父类自己的方法,没有调用子类重写的方法,所以就没有实现到多态的作用,我们再来在父类中试试加入virtual关键字看看:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class A  
  2. {  
  3. public:  
  4. virtual void printf(){  
  5.         cout<<"printf A"<<endl;  
  6.     }  
  7. };  
  8. class B : public A  
  9. {  
  10. public:  
  11.     void printf(){  
  12.         cout<<"printf B"<<endl;  
  13.     }  
  14. };  
  15. int main(int argc, const char * argv[])  
  16. {  
  17.     A *a = new B();  
  18.     a->printf();  
  19.     return 0;  
  20. }  

结果:

printf B

virtual是加入到父类中的,子类的代码没改变,main函数还是父类指针指向子类对象,结果终于可以打印到子类重写的方法了,所以证实了虚函数是用于多态中virtual修饰父类该重写的函数,确保父类指针调用子类对象时运行子类函数的。


二.纯虚函数

简单点说,纯虚函数就像java的接口,使用了纯虚函数的类不能被实例化,定义了纯虚函数的类不用写纯虚函数的实现,由子类实现,下面看代码:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class A  
  2. {  
  3. public:  
  4.     virtual void printf() =0;  
  5. };  
  6. void A::printf()//纯虚函数可以不写实现  
  7. {  
  8.     cout<<"printf A"<<endl;  
  9. }  
  10. class B : public A  
  11. {  
  12. public:  
  13.     void printf(){  
  14.         cout<<"printf B"<<endl;  
  15.     }  
  16. };  
  17. int main(int argc, const char * argv[])  
  18. {  
  19.     A *a =new A();//编译出错,纯虚函数的类不能实例化  
  20.     a->printf();  
  21.     return 0;  
  22. }  
virtual void printf() = 0;这是虚函数的写法,我在下面写了虚函数的实现 void A::printf(),其实写不写都没关系,写了也起不了作用= 。= 。然后我才main函数中尝试吧纯虚函数的类实例化,结果直接报错,说明纯虚函数是不能实例化的。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int main(int argc, const char * argv[])  
  2. {  
  3.     A *a =newB();//这里使用了多态  
  4.     a->printf();  
  5.     return 0;  
  6. }  

结果:

printf B


我把main函数的a指向了子类的对象,结果可以正确打印出子类的方法。由此说明了纯虚函数也是为多态服务的,它的作用是定义一个接口,让子类去实现。


三.虚基类

虚基类是c++独有的东西,因为c++中有多继承,也是关键字virtual相关的定义。

先来说说多继承,如果爷爷类(暂把父类的父类暂定为爷爷类= = ),父类继承自爷爷类。如果孙类继承自多个父类(听起来有点怪异),那么如果不使用虚基类,就会实例化多个爷爷类对象(越说越离奇),编译器会报错,说有歧义性。如果父类继承自虚基类,则可以解决多个父类不会实例化多个爷爷的问题,就是只有一个爷爷。

下面看代码:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class Grandfather{  
  2. public:  
  3.     int flag;  
  4.     Grandfather(){  
  5.         flag = 1;  
  6.     }  
  7. };  
  8. class Father1:publicGrandfather{  
  9. public:  
  10.     Father1(){  
  11.         flag = 2;  
  12.     }  
  13. };  
  14. class Father2:publicGrandfather{  
  15. public:  
  16.     Father2(){  
  17.         flag = 3;  
  18.     }  
  19. };  
  20. class Son:public Father1,publicFather2{  
  21. };  
  22. int main(int argc, const char * argv[])  
  23. {  
  24.     Son *son = new Son();  
  25.     cout<<son->flag<<endl;//这里编译错误,没法指定flag是指定那一个,歧义  
  26.     return 0;  
  27. }  

如果没有使用虚基类,多个父类继承自同一个爷爷类,就会产生歧义,到底是不是同一个爷爷?如果父类继承虚基类就不同了:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class Grandfather{  
  2. public:  
  3.     int flag;  
  4.     Grandfather(){  
  5.         flag = 1;  
  6.         cout<<"Grandfather flag = "<<flag <<endl;  
  7.     }  
  8. };  
  9. class Father1:virtualpublicGrandfather{  
  10. public:  
  11.     Father1(){  
  12.         flag = 2;  
  13.         cout<<"Father1 flag = "<<flag<<endl;  
  14.     }  
  15. };  
  16. class Father2:virtualpublicGrandfather{  
  17. public:  
  18.     Father2(){  
  19.         flag = 3;  
  20.         cout<<"Father2 flag = "<<flag<<endl;  
  21.     }  
  22. };  
  23. class Son:public Father1,publicFather2{  
  24. };  
  25. int main(int argc, const char * argv[])  
  26. {  
  27.     Son *son = new Son();  
  28.     cout<<son->flag<<endl;  
  29.     return 0;  
  30. }  

结果:

Grandfather flag = 1

Father1 flag = 2

Father2 flag = 3

3

现在,可以运行了,class Father2:virtual public Grandfather,就是继承虚基类的写法,爷爷对象只有一个,爷爷类的变量也只实例化了一次,那为什么最后打印出来的是3呢?看构造函数的顺序就可以看出来了,现在构造了爷爷类,再构造第一个继承的父类,最后继承第二个继承的父类,因此flag最后保持在第二个父类的修改值里了。


以上就是虚函数 ,纯虚函数, 虚基类知识点,虚函数 ,纯虚函数是为了多态服务,虚基类是为了只实例化一次基类存在的,如有说的不正确的地方,也请各位可以指正出来,共同学习。


0 0
原创粉丝点击