当前位置: 首页 > news >正文

海洋网站建设权威的大连网站建设

海洋网站建设,权威的大连网站建设,做设计什么兼职网站建设,wordpress修改功能小工具栏【1】表现形式:同样的调用语句有多种不同的表现形态 【2】分类:静态联编和动态联编 静态联编有函数重载(运算符重载是特殊的函数重载),模板 【3】重点说下动态联编 【3.1】动态联编的实现需要以下步骤: 有继承关系、父类函数有virtual关…

【1】表现形式:同样的调用语句有多种不同的表现形态

【2】分类:静态联编和动态联编

静态联编有函数重载(运算符重载是特殊的函数重载),模板

【3】重点说下动态联编

【3.1】动态联编的实现需要以下步骤:

  1. 有继承关系、
  2. 父类函数有virtual关键字
  3. 子类对父类对虚函数进行重写
  4. 父类的指针(引用)指向子类对象
  5. 通过父类的指针调用虚方法触发多态

【3.2】动态联编的编译器实现原理

当类中声明虚函数时,编译器会在类中生成一个虚函数表

虚函数表是一个存储类成员函数指针的数据结构

虚函数表是由编译器自动生成与维护的

virtual成员函数会被编译器放入虚函数表中 

存在虚函数时,够造对象时,对象中都有一个指向虚函数表的指针(vfptr指针) 

Vfptr   virtual function pointer 

如我们有下面的这个函数:

Class parent

{

Public:

Virtual func();

}

Class son:public parent

{

Public:

Virtual func();

}

void run(parent*p)

{

p->func();

}

编译器来确定func是否为虚函数

a.如果不是虚函数,编译器可以直接确定调用的函数,静态联编,根据parent类型来确定,编译完成之后就知道调用哪个函数地址了。

b.如果是虚函数,编译器根据对象p的vptr指针,所指的虚函数表中查找func函数,并调用,查找和调用时在执行时完成,动态联编。

说明1:

通过虚函数表指针VFPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。

说明2:

出于效率考虑,没有必要将所有成员函数都声明为虚函数

这里先给出一个图片来简单表示下,单继承的情况下,虚函数表的由来:

再给出一个证明虚函数的例子,以及探究虚函数表内部机构的例子:

参考链接:C++ 虚函数表解析---陈皓改进版_啊大1号的博客-CSDN博客_虚表 陈皓

#include<iostream>
using namespace std;
class Base {public:virtual void f() { cout << "Base::f" << endl; }virtual void g() { cout << "Base::g" << endl; }virtual void h() { cout << "Base::h" << endl; }
};typedef void(*Fun)(void);/*事实上,楼主写的一开始就是错的,虚函数表是类对象之间共享的,* 而非每个对象都保存了一份,楼主得到的也只是虚指针的地址,* 而非虚函数表的地址,事实上对虚函数指针的地址解引用得到的才* 是虚函数表的地址(因为虚函数指针指向虚函数表),以上经过理论和实际验证。
MyClass mc;//奇技淫巧、用int*只是因为32位系统中指针大小跟int相同//  pFun = (Fun)*( (int*)*(int*)(&mc)+1);//跟下面一样auto a1 = (int*)&mc;//找到虚指针的位置(地址)auto a2 = *a1;//得到虚指针的内容(指向的虚表的地址)auto a3 = (int*)a2;//得到虚表的地址pFun = (Fun)*(a3+1);//偏移得到虚表中某个虚函数的地址,解引用得到函数本身pFun();* */int main(){//虚函数表是用这个数组实现的 现在已经确定了cout<<sizeof(int)<<sizeof(long long)<<endl;Base b;Fun pFun = NULL;cout << "虚函数(表)指针 地址:" << (long long *)(&b) << endl;cout << "虚函数表 — 的地址:" << (long long*)*(long long *)(&b) << endl;
// Invoke the first virtual function// 这里才得到第一个虚函数的地址pFun = (Fun)*((long long *)*(long long *)(&b)+0);pFun();pFun = (Fun)*((long long *)*(long long *)(&b)+1);pFun();pFun = (Fun)*((long long *)*(long long *)(&b)+2);pFun();
}

这个时候你应该懂了吧。什么?还是有点晕。也是,这样的代码看着太乱了。没问题,让我画个图解释一下。如下所示:

注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符'\0'一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下,这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值如果是1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。

下面,我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。在比较之下,我们可以更加清楚地知道其内部的具体实现。

一般继承(无虚函数覆盖)

下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系:

请注意,在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例中,其虚函数表如下所示:

对于实例:Derive d; 的虚函数表如下:

我们可以看到下面几点:

1)虚函数按照其声明顺序放于表中。

2)父类的虚函数在子类的虚函数前面。

我相信聪明的你一定可以参考前面的那个程序,来编写一段程序来验证。

 一般继承(有虚函数覆盖)

 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。

为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:

 

我们从表中可以看到下面几点,

1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。

2)没有被覆盖的函数依旧。

这样,我们就可以看到对于下面这样的程序,

    Base *b = new Derive();b->f();

由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。

 

 

 

 

http://www.yayakq.cn/news/12915/

相关文章:

  • 服务器网站备案joomla与wordpress学哪个好
  • 西安网站建设eliwe苏州推荐网络公司建网站
  • 企业对比网站微盟小程序商城
  • 云南品牌网站开发广东省白云区属于哪个市
  • 自己可以做公司网站吗优化网站排名推荐公司
  • php网站开发师条件什么软件推广效果好
  • 邢台专业做网站费用珠海网站建设哪家权威
  • 建筑类电商网站介绍网站建设规划书结构
  • 网站开发职务苏州哪里做网站好
  • 包装材料东莞网站建设var_dump调试wordpress
  • 蓝色网站素材烘焙食品网站建设需求分析
  • 中国建设银行网站 个人做网站着用什么软件
  • 成都企业网站建设方案西安建设工程交易中心网站
  • js 网站头部固定推广普通话作文
  • 网站建设费 科研 设备费重庆九龙坡营销型网站建设公司哪家好
  • wordpress 非插件七牛cdn全站加速合肥专业做网站的公司
  • 网站建设公司(推荐乐云践新)网站建设百度索引
  • 高端学校网站建设做废钢铁生意在哪个网站了解
  • 文档阅读网站模板下载网站经营性备案条件
  • 阿里云网站备案好了 怎么建站电子商城网站建议书
  • 公司和个人均不能备案论坛类网站长沙专门做网站公司有哪些
  • 建站网站设计西安网站建设设计
  • 智能网站建设背景仿美团网站开发
  • 男女插孔做暖暖网站大全越南注册公司流程和费用
  • 深圳模板网站wordpress幻灯片 设置
  • 怎么不花钱建立网站大连市公众平台网站
  • 卖域名的网站要怎么做一诺千金 网站建设
  • 网站服务器建设教程WordPress怎么设置2个菜单
  • 什么颜色做网站好看中企动力企业邮箱手机app
  • 网站设计成功案例承德兴隆建设局网站