http://blog.csdn.net/djh512/article/details/8973606

1.virtual关键字主要是什么作用?

c++中的函数调用默认不适用动态绑定。要触发动态绑定,必须满足两个条件:第一,指定为虚函数;第二,通过基类类型的引用或指针调用。
由此可见,virtual主要主要是实现动态绑定。
 

2.那些情况下可以使用virtual关键字?

virtual可用来定义类函数和应用到虚继承。
 
友元函数 构造函数 static静态函数 不能用virtual关键字修饰;
普通成员函数 和析构函数 可以用virtual关键字修饰;
 
3.virtual函数的效果
  1. class GrandFather
  2. {
  3. public:
  4. GrandFather() {}
  5. virtual void fun()
  6. {
  7. cout << "GrandFather call function!" << endl;
  8. }
  9. };
  10. class Father : public GrandFather
  11. {
  12. public:
  13. Father() {}
  14. void fun()
  15. {
  16. cout << "Father call function!" << endl;
  17. }
  18. };
  19. class Son : public Father
  20. {
  21. public:
  22. Son() {}
  23. void fun()
  24. {
  25. cout << "Son call function!" << endl;
  26. }
  27. };
  28. void print(GrandFather* father)
  29. {
  30. father->fun();
  31. }
  32. int _tmain(int argc, _TCHAR* argv[])
  33. {
  34. Father * pfather = new Son;
  35. pfather->fun();
  36. GrandFather * pgfather = new Father;
  37. print(pgfather);
  38. return 0;
  39. }

输出为 Son call function

                    Father call function
 

4.virtual的继承性

只要基函数定义了virtual,继承类的该函数也就具有virtual属性
        即 GrandFather Father Son同时定义virtual void fun()与GrandFather一个定义virtual void fun效果是一样的

5.虚析构函数

  1. class GrandFather
  2. {
  3. public:
  4. GrandFather() {}
  5. virtual void fun()
  6. {
  7. cout << "GrandFather call function!" << endl;
  8. }
  9. ~GrandFather()
  10. {
  11. cout << "GrandFather destruction!" << endl;
  12. }
  13. };
  14. class Father : public GrandFather
  15. {
  16. public:
  17. Father() {}
  18. void fun()
  19. {
  20. cout << "Father call function!" << endl;
  21. }
  22. ~Father()
  23. {
  24. cout << "Father destruction!" << endl;
  25. }
  26. };
  27. class Son : public Father
  28. {
  29. public:
  30. Son() {}
  31. void fun()
  32. {
  33. cout << "Son call function!" << endl;
  34. }
  35. ~Son()
  36. {
  37. cout << "Son destruction!" << endl;
  38. }
  39. };
  40. void print(GrandFather* p)
  41. {
  42. p->fun();
  43. }
  44. int _tmain(int argc, _TCHAR* argv[])
  45. {
  46. Father * pfather = new Son;
  47. delete pfather;
  48. return 0;
  49. }

以上代码输出:Father destruction!

                             GrandFather destruction!
   执行了Son的构造函数,没执行Son的析构函数,故把GrandFather的析构函数设置为virtual
   则输出: Son destruction!
                Father Destruction!
               GrandFather destruction!

6. 纯虚函数

纯虚函数定义如下:
  1. class GrandFather
  2. {
  3. public:
  4. GrandFather() {}
  5. virtual void fun() = 0
  6. {
  7. cout << "GrandFather call function!" << endl;
  8. }
  9. virtual ~GrandFather()
  10. {
  11. cout << "GrandFather destruction!" << endl;
  12. }
  13. };

纯虚函数为后代类提供可覆盖的接口,但这个类中的版本决不会调用。

含有(或继续)一个或多个纯虚函数的类是抽象基类,抽象基类不能实例化!
继承类只有重写这个接口才能被实例化
 

7.虚继承

虚继承主要解决交叉继承带来的问题。这里给出一片参考文章c++虚继承
给一个例子如下
  1. class GrandFather
  2. {
  3. public:
  4. GrandFather() {}
  5. void fun()
  6. {
  7. cout << "GrandFather call function!" << endl;
  8. }
  9. virtual ~GrandFather()
  10. {
  11. cout << "GrandFather destruction!" << endl;
  12. }
  13. };
  14. class Father1 : public GrandFather
  15. {
  16. public:
  17. Father1() {}
  18. void fun()
  19. {
  20. cout << "Father call function!" << endl;
  21. }
  22. };
  23. class Father2 : public GrandFather
  24. {
  25. public:
  26. Father2() {}
  27. void fun()
  28. {
  29. cout << "Father call function!" << endl;
  30. }
  31. };
  32. class Son : public Father1, public Father2
  33. {
  34. public:
  35. Son() {}
  36. //void fun()
  37. //{
  38. //  cout << "Son call function!" << endl;
  39. //}
  40. };
  41. void print(GrandFather* p)
  42. {
  43. p->fun();
  44. }
  45. int _tmain(int argc, _TCHAR* argv[])
  46. {
  47. Son* son = new Son;
  48. son->fun();
  49. return 0;
  50. }

编译时会提示报错对fun的访问不明确

如果Father1和Father2都用虚继承继承GrandFather类则可以解决这个问题
 

8. 构造函数和析构函数中的虚函数

如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本
 
9.虚函数的实现机制
关于虚函数的实现机制,这里给出一篇认为写得蛮清楚的文章。c++虚函数实现机制
 

10.小结

关于virtual关键字的用法总结如上,有错误或者总结不到位的情况请能帮本人指出!
 
11.例子
  1. class classA
  2. {
  3. public:
  4. classA()
  5. {
  6. clear();
  7. }
  8. virtual ~classA()
  9. {
  10. }
  11. void clear()
  12. {
  13. memset(this , 0 , sizeof(*this));
  14. }
  15. virtual void func()
  16. {
  17. printf("func\n");
  18. }
  19. };
  20. class classB : public classA
  21. {
  22. };
  23. int main(void)
  24. {
  25. classA oa;
  26. classB ob;
  27. classA * pa0 = &oa;
  28. classA * pa1 = &ob;
  29. classB * pb = &ob;
  30. oa.func(); // 1
  31. ob.func(); // 2
  32. pa0->func(); // 3
  33. pa1->func(); // 4
  34. pb->func(); // 5
  35. return 0;
  36. }

补充一个例子,这个程序输出依次是

func
func
出错
func
func
 
谈谈我的理解,当
classA oa;
oa.func();
不存在动态调用的过程,所以func虽然是虚函数,但是函数调用不通过虚表访问,所以即使

  1. memset(this , 0 , sizeof(*this));

找不到虚表地址也没有关系

在执行classB ob;的时候,注意memset的是classA的地址,所有ob的虚表是存在的
即是如下,通过指针或引用(动态绑定)访问oa的func函数(需要从虚表访问),会出错
访问ob的func和函数,无论静态访问还是动态访问,都不会出错
 
当把classB的代码改成如下时

  1. class classB : public classA
  1. <pre name="code" class="cpp" style="font-weight: bold;">{</pre><pre name="code" class="cpp" style="font-weight: bold;">        classB()
  2. {
  3. clear();
  4. }
  5. virtual ~classB()
  6. {
  7. }
  8. void clear()
  9. {
  10. memset(this , 0 , sizeof(*this));
  11. }</pre><br>
  12. <pre></pre>
  13. <pre name="code" class="cpp" style="font-weight: bold;">};</pre>输出为
func
func
出错
出错
出错

浅析c++中virtual关键字的更多相关文章

  1. 浅析C#中new、override、virtual关键字的区别

    Virtual : virtual 关键字用于修饰方法.属性.索引器或事件声明,并使它们可以在派生类中被重写. 默认情况下,方法是非虚拟的.不能重写非虚方法. virtual 修饰符不能与 stati ...

  2. 浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  3. [转载]浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  4. 【转】浅析Java中的final关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. ...

  5. 浅析Java中的native关键字

    浅析Java中的native关键字 native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中.Java语言本身不能对操作系统底层进 ...

  6. C#基础知识五之abstract virtual关键字

    abstract 用关键字abstract修饰的类叫做抽象类,且只能作为基类,也不能实例化. 用abstract定义的抽象类中不一定只包含抽象方法 ,可以包含非抽象方法. abstract定义的方法一 ...

  7. 浅析JS中的模块规范(CommonJS,AMD,CMD)////////////////////////zzzzzz

    浅析JS中的模块规范(CommonJS,AMD,CMD)   如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已.     ...

  8. asp.net中virtual和abstract的区别

    这篇文章主要介绍了asp.net中virtual和abstract的区别,较为详细的分析了virtual与abstract的概念与具体用法,并以实例的形式予以总结归纳,需要的朋友可以参考下 本文实例分 ...

  9. asp.net中virtual和abstract的区别分析

    这篇文章主要介绍了asp.net中virtual和abstract的区别,较为详细的分析了virtual与abstract的概念与具体用法,并以实例的形式予以总结归纳,需要的朋友可以参考下 本文实例分 ...

随机推荐

  1. OI 助手 | 简洁快速的 OI 工具箱 (原 竞赛目录生成)

    原竞赛目录生成 (4.0 版本前) 开发者:abc2237512422 OI 助手是一个轻量简洁的 OI 工具箱.你可以使用它来快速进行 OI 竞赛中一些繁琐的操作,例如生成竞赛目录.对拍.它为你省去 ...

  2. 【LOJ】#2186. 「SDOI2015」道路修建

    题解 就是线段树维护一下转移矩阵 分成两种情况,一种是前面有两个联通块,一种是前面有一个联通块 从一个联通块转移到一个联通块 也就是新加一列的三个边选其中两条即可 从一个联通块转移到两个联通块 不连竖 ...

  3. 016 jquery中html与val得到使用

    1.属性操作 2.设置html . 3.程序(关于html) <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  4. java学习之借书系统

    实现的图书借阅系统要处理用户输入的非法参数,并引导用户正确使用 测试结果: 主要目的就是练习异常处理中的Exception类的使用 使用的相关语法 try{ //可能产生异常的代码块 }catch(E ...

  5. 各jQuery选择器的用法(转)

    这篇文章来介绍下jQuery的选择器. 选择器是jQuery的根基,在jQuery中,对事件处理.遍历DOM和Ajax操作都依 赖于选择器.因此,如果能熟练的使用选择器,不仅能简化代码,而且可以达到事 ...

  6. 常用工具(Windows版本)

    为原有版本修改为markdown后的更新,这个编辑器真心不错,只需要把原来喜欢的表格改成列表即可. 代码工具 代码管理工具 SourceTree:支持windows和mac跨平台使用的git图形化客户 ...

  7. mysql存储过程之游标

    MySQL5 中添加了存储过程的支持.     大多数SQL语句都是针对一个或多个表的单条语句.并非所有的操作都怎么简单.经常会有一个完整的操作需要多条才能完成     存储过程简单来说,就是为以后的 ...

  8. 循序渐进学.Net Core Web Api开发系列【8】:访问数据库(基本功能)

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇讨论如 ...

  9. 100BASE-TX / 100BASE-T4/100BASE-FX

    IEEE标准共有以下几种:10BASE-5:粗缆.最大传输距离500米,使用AUI连接器连接或使用收发器电缆和收发器(MAU)进行连接.10BASE-2:细缆.实际传输距离为185米,使用BNC连接器 ...

  10. 系统的Drawable(一)

    系统的Drawable(一) 学习自 <Android 开发艺术探索> <官方文档> https://www.cnblogs.com/popfisher/p/6238119.h ...