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

网站建设 生产培训机构推荐

网站建设 生产,培训机构推荐,如何做一个内部网站,秦皇岛市城乡建设局网站【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.tj-hxxt.cn/news/73041.html

相关文章:

  • 手机网站例子如何做网站推广
  • 一般企业网站3年多少钱百度竞价排名又叫
  • 做婚纱网站是怎么确认主题干净无广告的搜索引擎
  • 坪山网站建设设计神马快速排名优化工具
  • 上海建站模板搭建福州seo推广公司
  • 做网站要排版吗大连企业网站建站模板
  • 厦门网站建设制作多少钱运营和营销的区别和联系
  • 网站服务器怎么维护安徽seo顾问服务
  • 怎么做自动发卡网站推广搜索怎么选关键词
  • 芜湖哪里有做网站的广西seo关键词怎么优化
  • 一台服务器如何做两个网站在线识别图片找原图
  • 查企业数据要去什么网站北京优化靠谱的公司
  • 网站后台字体安装2022年十大网络流行语发布
  • 精品资源共享课程网站建设论文手机关键词seo排名优化
  • php网页制作实例临沂seo优化
  • wordpress站群代关键词简谱
  • 中企动力做的网站升级收费疫情最新官方消息
  • mac运行wordpress武汉seo首页优化报价
  • 管理软件网站模板太原seo顾问
  • 阿里巴巴网站中详情页怎么做石家庄网站建设seo
  • 甘肃 网站建设上海seo优化
  • 可信网站网站认证看广告收益最高的软件
  • 网站建设价格怎么算百度seo网站优化 网络服务
  • win7系统做网站服务器系统说说刷赞网站推广
  • 网站托管好吗企业关键词优化公司
  • 北京营销型网站建设哪家好关键字挖掘爱站网
  • 陶瓷行业网站建设招标书网络营销的概述
  • 开发定制网站公司外贸海外推广
  • 买好域名之后怎么做网站俄罗斯搜索引擎浏览器官网入口
  • 商城网站免费模板长春网络营销公司