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. python多个分割符split字符串

    python中string自带的split不支持多个分隔符同时切分,用正则 import re line='hello,world' lineLists = re.split('[,,..??]',l ...

  2. 使用 Application Loader提交IPA文件到苹果市场

    打包.导出ipa包后剩下的就是要将ipa包推到appstore.Application Loader是苹果提供的ipa包提交工具. 1.启动Application Loader 打开xcode,在xc ...

  3. 有用的Python模块 - pprint

    当想在终端打印一个很大的字典或者一个很长的列表时,总是被print打印出来的效果气懵在电脑前,现在有pprint就不用担心啦. 最直接的使用方式就是 import pprint pprint.ppri ...

  4. Codeforces 379F New Year Tree 树的直径的性质推理

    New Year Tree 我们假设当前的直径两端为A, B, 那么现在加入v的两个儿子x, y. 求直径的话我们可以第一次dfs找到最远点这个点必定为直径上的点, 然而用这个点第二次dfs找到最远点 ...

  5. CI框架与Thinkphp框架的一些区别

    初学CI框架遇到的一些问题,与Thinkphp框架对比的不同之处. system             是框架核心 application        是项目目录 index.php        ...

  6. 格式化输出函数:printf 那些事 (C语言)

    printf函数提供格式化输出转换 函数包含在头文件  <stdio.h>  中 #include <stdio.h> ...... 函数的原型在头文件的声明为 _CRTIMP ...

  7. ref:Spring JdbcTemplate+JdbcDaoSupport实例

    ref:https://www.yiibai.com/spring/spring-jdbctemplate-jdbcdaosupport-examples.html 在Spring JDBC开发中,可 ...

  8. Android Socket

    Android Socket 参考资料 菜鸟教程 怎么理解TCP的面向连接和UDP的无连接 https://www.cnblogs.com/xiaomayizoe/p/5258754.html htt ...

  9. Codeforces.997C.Sky Full of Stars(容斥 计数)

    题目链接 那场完整的Div2(Div1 ABC)在这儿.. \(Description\) 给定\(n(n\leq 10^6)\),用三种颜色染有\(n\times n\)个格子的矩形,求至少有一行或 ...

  10. ScrollView中嵌套GridView,Listview的办法

    按照android的标准,ScrollView中是不能嵌套具有滑动特性的View的,但是有时如果设计真的有这样做的需要,或者为了更方便简单的实现外观(比如在外在的大布局需要有滑动的特性,并且内部有类似 ...