zoukankan      html  css  js  c++  java
  • 浅析c++中virtual关键字

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

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

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

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

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

    4.virtual的继承性

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

    5.虚析构函数

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

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

    6. 纯虚函数

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

    纯虚函数为后代类提供可覆盖的接口,但这个类中的版本决不会调用。
    含有(或继续)一个或多个纯虚函数的类是抽象基类,抽象基类不能实例化!
    继承类只有重写这个接口才能被实例化
     

    7.虚继承

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

    编译时会提示报错对fun的访问不明确
    如果Father1和Father2都用虚继承继承GrandFather类则可以解决这个问题
     

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

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

    10.小结

    关于virtual关键字的用法总结如上,有错误或者总结不到位的情况请能帮本人指出!
     
    11.例子
    [cpp] view plaincopy
     
    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 ");  
    18.     }  
    19. };  
    20.   
    21. class classB : public classA  
    22. {  
    23. };  
    24.   
    25. int main(void)  
    26. {  
    27.     classA oa;  
    28.     classB ob;  
    29.     classA * pa0 = &oa;  
    30.     classA * pa1 = &ob;  
    31.     classB * pb = &ob;  
    32.     oa.func(); // 1  
    33.     ob.func(); // 2  
    34.     pa0->func(); // 3  
    35.     pa1->func(); // 4  
    36.     pb->func(); // 5  
    37.     return 0;  
    38. }  
    补充一个例子,这个程序输出依次是
    func
    func
    出错
    func
    func
     
    谈谈我的理解,当
    classA oa;
    oa.func();
    不存在动态调用的过程,所以func虽然是虚函数,但是函数调用不通过虚表访问,所以即使
    [cpp] view plaincopy
     
    1. memset(this , 0 , sizeof(*this));  
    找不到虚表地址也没有关系
    在执行classB ob;的时候,注意memset的是classA的地址,所有ob的虚表是存在的
    即是如下,通过指针或引用(动态绑定)访问oa的func函数(需要从虚表访问),会出错
    访问ob的func和函数,无论静态访问还是动态访问,都不会出错
     
    当把classB的代码改成如下时
    [cpp] view plaincopy
     
    1. class classB : public classA  
    [cpp] view plaincopy
     
    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
    出错
    出错
    出错
  • 相关阅读:
    创建对象_原型(Prototype)模式_深拷贝
    创建对象_工厂方法(Factory Method)模式 与 静态工厂方法
    创建对象——单例(Singleton)模式
    模板方法模式
    移除HTML5 input在type="number"时的上下小箭头
    颜色名列表
    什么是盒模型?
    JQuery中$.ajax()方法参数详解
    zsh下docker命令tab补全方法
    ubuntu14.04 搭建gitlab服务
  • 原文地址:https://www.cnblogs.com/wanqieddy/p/4369367.html
Copyright © 2011-2022 走看看