浅析c++中virtual关键字

来源:互联网 发布:淘宝视频代看怎么做 编辑:程序博客网 时间:2024/05/22 20:27

原文出自:http://blog.csdn.net/djh512/article/details/8973606#t4


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

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

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

virtual可用来定义类函数和应用到虚继承。

友元函数 构造函数 static静态函数 不能用virtual关键字修饰;
普通成员函数 和析构函数 可以用virtual关键字修饰;

3.virtual函数的效果
class GrandFather  {  public:      GrandFather() {}      virtual void fun()      {          cout << "GrandFather call function!" << endl;      }  };    class Father : public GrandFather  {  public:       Father() {}       void fun()       {           cout << "Father call function!" << endl;       }  };      class Son : public Father  {  public:      Son() {}      void fun()      {          cout << "Son call function!" << endl;      }  };    void print(GrandFather* father)  {      father->fun();  }    int _tmain(int argc, _TCHAR* argv[])  {      Father * pfather = new Son;          pfather->fun();          GrandFather * pgfather = new Father;          print(pgfather);      return 0;  }  

输出为 Son call function
                    Father call function

4.virtual的继承性

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

5.虚析构函数

class GrandFather  {  public:      GrandFather() {}      virtual void fun()      {          cout << "GrandFather call function!" << endl;      }        ~GrandFather()       {          cout << "GrandFather destruction!" << endl;      }  };    class Father : public GrandFather  {  public:      Father() {}      void fun()      {          cout << "Father call function!" << endl;      }        ~Father()      {          cout << "Father destruction!" << endl;      }  };      class Son : public Father  {  public:      Son() {}      void fun()      {          cout << "Son call function!" << endl;      }         ~Son()      {          cout << "Son destruction!" << endl;      }  };    void print(GrandFather* p)  {      p->fun();  }    int _tmain(int argc, _TCHAR* argv[])  {      Father * pfather = new Son;      delete pfather;      return 0;  }  


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

6. 纯虚函数

纯虚函数定义如下:
class GrandFather  {  public:      GrandFather() {}      virtual void fun() = 0      {          cout << "GrandFather call function!" << endl;      }        virtual ~GrandFather()       {          cout << "GrandFather destruction!" << endl;      }  };  

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

7.虚继承

虚继承主要解决交叉继承带来的问题。这里给出一片参考文章c++虚继承。
给一个例子如下
class GrandFather  {  public:      GrandFather() {}      void fun()      {          cout << "GrandFather call function!" << endl;      }        virtual ~GrandFather()       {          cout << "GrandFather destruction!" << endl;      }  };    class Father1 : public GrandFather  {  public:      Father1() {}      void fun()      {          cout << "Father call function!" << endl;      }        };    class Father2 : public GrandFather  {  public:      Father2() {}      void fun()      {          cout << "Father call function!" << endl;      }    };      class Son : public Father1, public Father2  {  public:      Son() {}      //void fun()      //{      //  cout << "Son call function!" << endl;      //}  };    void print(GrandFather* p)  {      p->fun();  }    int _tmain(int argc, _TCHAR* argv[])  {      Son* son = new Son;      son->fun();      return 0;  }  
编译时会提示报错对fun的访问不明确
如果Father1和Father2都用虚继承继承GrandFather类则可以解决这个问题

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

如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本

9.虚函数的实现机制
关于虚函数的实现机制,这里给出一篇认为写得蛮清楚的文章。c++虚函数实现机制

10.小结

关于virtual关键字的用法总结如上,有错误或者总结不到位的情况请能帮本人指出!

11.例子
class classA  {   public:      classA()      {          clear();      }      virtual ~classA()      {      }      void clear()      {          memset(this , 0 , sizeof(*this));      }      virtual void func()      {          printf("func\n");      }  };    class classB : public classA  {  };    int main(void)  {      classA oa;      classB ob;      classA * pa0 = &oa;      classA * pa1 = &ob;      classB * pb = &ob;      oa.func(); // 1      ob.func(); // 2      pa0->func(); // 3      pa1->func(); // 4      pb->func(); // 5      return 0;  }  

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

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

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

当把classB的代码改成如下时
class classB : public classA  [cpp] view plaincopy<pre name="code" class="cpp" style="font-weight: bold;">{</pre><pre name="code" class="cpp" style="font-weight: bold;">        classB()      {          clear();      }      virtual ~classB()      {      }      void clear()      {          memset(this , 0 , sizeof(*this));      }

func
func
出错
出错
出错


0 0
原创粉丝点击