C++ virtual关键字深入理解

对virtual关键的理解需要结合其应用场景来分析。以下从多个角度来深入理解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 _t main(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
classB()
 {  clear();
 }
 virtual ~classB()
 {
 }
 void clear()
 {  memset(this , 0 , sizeof(*this));
 }
 输出为

func
func
出错
出错
出错
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值