深入理解c++中virtual关键字

  • 更新时间:2022-09-23 09:39:24
  • 编辑:池紫云
我们帮大家精选了c++相关的编程文章,网友阚尤莲根据主题投稿了本篇教程内容,涉及到virtual、virtual相关内容,已被920网友关注,内容中涉及的知识点可以在下方直接下载获取。

参考资料

正文内容

virtual

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.虚函数的实现机制 关于虚函数的实现机制,我们以后在介绍。

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

 

<PRE style="FONT-WEIGHT: bold" class=cpp name="code">{</PRE><PRE style="FONT-WEIGHT: bold" class=cpp name="code">        classB()  {   clear();  }  virtual ~classB()  {  }  void clear()  {      memset(this , 0 , sizeof(*this));  }</PRE><BR> <PRE></PRE> <PRE style="FONT-WEIGHT: bold" class=cpp name="code">};</PRE>输出为

func func 出错 出错 出错

 

c++相关教程

  • C++保存txt文件实现方法代码实例

    这篇文章主要介绍了C++保存txt文件实现方法代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    发布时间:2022-04-08

  • C++子类父类成员函数的覆盖和隐藏实例详解

    C++子类父类成员函数的覆盖和隐藏实例详解

    给大家整理一篇关于C++的教程,这篇文章主要介绍了C++子类父类成员函数的覆盖和隐藏实例详解的相关资料,需要的朋友可以参考下

    发布时间:2022-09-12

  • C语言实现C++继承和多态的实例内容

    本文主要给大家简单讲诉了C和C++的区别以及如何使用C语言模拟实现C++继承和多态,并附上示例代码,是篇相当不错的文章,推荐给喜欢C语言的小伙伴们

    发布时间:2021-06-10

  • c++11&14-多线程要点汇总

    这篇文章主要介绍了c++11&14-多线程的使用方法,文中代码非常详细,方便大家更好的参考和学习,感兴趣的朋友快来了解下

    发布时间:2022-04-08

  • C/C++函数调用栈的实现方法

    给网友朋友们带来一篇关于C++的教程,这篇文章主要介绍了C/C++函数调用栈的实现方法,可实现一个简单的脚本解释器,具有一定的参考借鉴价值,需要的朋友可以参考下

    发布时间:2022-06-22

  • 如何理解C++ 临时变量的常量性

    这篇文章主要介绍了如何理解C++ 临时变量的常量性,帮助大家更好的理解和学习c++ 变量,感兴趣的朋友可以了解下

    发布时间:2022-04-02

  • VC++中进程与多进程管理的方法详解

    这篇文章主要介绍了VC++中进程与多进程管理的方法,以实例形式详细分析了进程与多进程管理中所涉及的进程、子进程、进程的互斥运行与进程的结束等概念与具体实现方法,非常具有参考借鉴价值,需要的朋友可以参考下

    发布时间:2021-05-28

用户留言