谷歌SEO

谷歌SEO

Products

当前位置:首页 > 谷歌SEO >

漳州企业如何通过亦庄网站建设提升品牌影响力?

96SEO 2026-02-20 07:36 0


3.1.多态与虚表

语言中直接支持面向对象程序设计的部分。

漳州企业如何通过亦庄网站建设提升品牌影响力?

对于各种支持的底层实现机制。

直接支持面向对象程序设计包括了构造函数、析构函数、多态、虚函数等等这些内容在很多书籍上都有讨论也是C最被人熟知的地方特性。

而对象模型的底层实现机制却是很少有书籍讨论的。

对象模型的底层实现机制并未标准化不同的编译器有一定的***来设计对象模型的实现细节。

在我看来对象模型研究的是对象在存储上的空间与时间上的更优并对C面向对象技术加以支持如以虚指针、虚表机制支持多态特性。

2.文章内容简介

这篇文章主要来讨论C对象在内存中的布局属于第二个概念的研究范畴。

而C直接支持面向对象程序设计部分则不多讲。

文章主要内容如下

虚函数表解析。

含有虚函数或其父类含有虚函数的类编译器都会为其添加一个虚函数表,vptr先了解虚函数表的构成有助对C对象模型的理解。

虚基类表解析。

虚继承产生虚基类表(vbptr)虚基类表的内容与虚函数表完全不同我们将在讲解虚继承时介绍虚函数表。

对象模型概述介绍简单对象模型、表格驱动对象模型以及非继承情况下的C对象模型。

继承下的C对象模型。

分析C类对象在下面情形中的内存布局

单继承子类单一继承自父类分析了子类重写父类虚函数、子类定义了新的虚函数情况下子类对象内存布局。

多继承子类继承于多个父类分析了子类重写父类虚函数、子类定义了新的虚函数情况下子类对象内存布局同时分析了非虚继承下的菱形继承。

虚继承分析了单一继承下的虚继承、多重继承下的虚继承、重复继承下的虚继承。

理解对象的内存布局之后我们可以分析一些问题

C封装带来的布局成本是多大由空类组成的继承层次中每个类对象的大小是多大

至于其他与内存有关的知识我假设大家都有一定的了解如内存对齐指针操作等。

本文初看可能晦涩难懂要求读者有一定的C基础对概念一有一定的掌握。

3.理解虚函数表

C中虚函数的作用主要是为了实现多态机制。

多态简单来说是指在继承层次中父类的指针可以具有多种形态——当它指向某个子类对象时通过它能够调用到子类的函数而非父类的函数。

class

prt2-print();//调用Drive1::print()

prt3-print();//调用Drive2::print()这是一种运行期多态即父类指针唯有在程序运行时才能知道所指的真正类型是什么。

这种运行期决议是通过虚函数表来实现的。

3.2.使用指针访问虚表

setI(){cout调用了虚函数Base::setI();}virtual

~Base(){}private:int

baseI;};当一个类本身定义了虚函数或其父类有虚函数时为了支持多态机制编译器将为该类添加一个虚函数指针vptr。

虚函数指针一般都放在对象内存布局的第一个位置上这是为了保证在多层继承或多重继承的情况下能以最高效率取到虚函数表。

当vprt位于对象内存最前面时对象的地址即为虚函数指针地址。

我们可以取得虚函数指针的地址

Base

类型取得了虚函数指针的地址。

虚函数指针指向虚函数表,虚函数表中存储的是一系列虚函数的地址虚函数地址出现的顺序与类中虚函数声明的顺序一致。

对虚函数指针地址值可以得到虚函数表的地址也即是虚函数表第一个虚函数的地址:

typedef

通过地址调用虚函数Base::print();vfunc();我们把虚表指针的值取出来

(int)(b)它是一个地址虚函数表的地址把虚函数表的地址强制转换成

int*

这样我们就取得了类中的第一个虚函数我们可以通过函数指针访问它。

运行结果

)(*(int*)(b)1)同样可以通过函数指针访问它这里留给读者自己试验。

到目前为止我们知道了类中虚表指针vprt的由来知道了虚函数表中的内容以及如何通过指针访问虚函数表。

下面的文章中将常使用指针访问对象内存来验证我们的C对象模型以及讨论在各种继承情况下虚表指针的变化先把这部分的内容消化完再接着看下面的内容。

4.对象模型概述

functions:static、nonstatic和virtual:

class

};那么这个类在内存中将被如何表示5种数据都是连续存放的吗如何布局才能支持C多态

4.1.简单对象模型

这个模型非常地简单粗暴。

在该模型下对象由一系列的指针组成每一个指针都指向一个数据成员或成员函数也即是说每个数据成员和成员函数在类中所占的大小是相同的都为一个指针的大小。

这样有个好处——很容易算出对象的大小不过赔上的是空间和执行期效率。

想象一下如果我们的Point3d类是这种模型将会比C语言的struct多了许多空间来存放指向函数的指针而且每次读取类的数据成员都需要通过再一次寻址——又是时间上的消耗。

4.2.表格驱动模型

这个模型在简单对象模型的基础上又添加一个间接层它把类中的数据分成了两个部分数据部分与函数部分并使用两张表格一张存放数据本身一张存放函数的地址也即函数比成员多一次寻址而类对象仅仅含有两个指针分别指向上面这两个表。

这样看来对象的大小是固定为两个指针大小。

这个模型也没有用于实际应用于真正的C编译器上。

概述在此模型下nonstatic

数据成员被置于每一个类对象中而static数据成员被置于类对象之外。

static与nonstatic函数也都放在类对象之外而对于virtual

每个类生成一个表格称为虚表virtual

table简称vtbl。

虚表中存放着一堆指针这些指针指向该类每一个虚函数。

虚表中的函数地址将按声明时的顺序排列不过当子类有多个重载函数时例外后面会讨论。

每个类对象都拥有一个虚表指针(vptr)由编译器为其生成。

虚表指针的设定与重置皆由类的复制控制也即是构造函数、析构函数、赋值操作符来完成。

vptr的位置为编译器决定传统上它被放在所有显示声明的成员之后不过现在许多编译器把vptr放在一个类对象的最前端。

关于数据成员布局的内容在后面会详细分析。

另外虚函数表的前面设置了一个指向type_info的指针用以支持RTTIRun

Time

Identification运行时类型识别。

RTTI是为多态而生成的信息包括对象继承关系对象本身的描述等只有具有虚函数的对象在会生成。

Base

b(1000);可见对象b含有一个vfptr即vprt。

并且只有nonstatic数据成员被放置于对象内。

我们展开vfprt

vfptr中有两个指针类型的数据地址第一个指向了Base类的析构函数第二个指向了Base的虚函数print顺序与声明顺序相同。

void

*((RTTICompleteObjectLocator*)*((int*)*(int*)(p)

1));string

classname(str.pTypeDescriptor-name);classname

classname.substr(4,

endl;//输入static函数的地址p.countI();//先调用函数以产生一个实例cout

p.countI

*)(p)取得虚函数表的地址type_info信息的确存在于虚表的前一个位置。

通过((int)(int*)(p)

1))取得type_infn信息并成功获得类的名称的Base虚函数表的第一个函数是析构函数。

虚函数表的第二个函数是虚函数print()取得地址后通过地址调用它而非通过对象验证正确虚表指针的下一个位置为nonstatic数据成员baseI。

可以看到static成员函数的地址段位与虚表指针、baseI的地址段位不同。

好的至此我们了解了非继承下类对象五种数据在内存上的布局也知道了在每一个虚函数表前都有一个指针指向type_info负责对RTTI的支持。

而加入继承后类对象在内存中该如何表示呢

5.1.单继承

DeriveI(d){};//overwrite父类虚函数virtual

void

一个派生类如何在机器层面上塑造其父类的实例呢在简单对象模型中可以在子类对象中为每个类子对象分配一个指针。

如下图

简单对象模型的缺点就是因间接性导致的空间存取时间上的额外负担优点则是类的大小是固定的基类的改动不会影响子类对象的大小。

在表格驱动对象模型中我们可以为子类对象增加第三个指针基类指针(bptr)基类指针指向指向一个基类表(base

class

table),同样的由于间接性导致了空间和存取时间上的额外负担优点则是无须改变子类对象本身就可以更改基类。

表格驱动模型的图就不再贴出来了。

在C对象模型中对于一般继承这个一般是相对于虚拟继承而言若子类重写overwrite了父类的虚函数则子类虚函数将覆盖虚表中对应的父类虚函数(注意子类与父类拥有各自的一个虚函数表)若子类并无overwrite父类虚函数而是声明了自己新的虚函数则该虚函数地址将扩充到虚函数表最后在vs中无法通过监视看到扩充的结果不过我们通过取地址的方法可以做到子类新的虚函数确实在父类子物体的虚函数表末端。

而对于虚继承若子类overwrite父类虚函数同样地将覆盖父类子物体中的虚函数表对应位置而若子类声明了自己新的虚函数则编译器将为子类增加一个新的虚表指针vptr这与一般继承不同,在后面再讨论。

typedef

endl;//vprt[1]析构函数无法通过地址调用故手动输出cout

[1]

单继承中一般继承子类会扩展父类的虚函数表。

在多继承中子类含有多个父类的子对象该往哪个父类的虚函数表扩展呢当子类overwrite了父类的函数需要覆盖多个父类的虚函数表吗

子类的虚函数被放在声明的第一个基类的虚函数表中。

overwrite时所有基类的print()函数都被子类的print()函数覆盖。

内存布局中父类按照其声明顺序排列。

其中第二点保证了父类指针指向子类对象时总是能够调用到真正的函数。

class

endl;//vprt[0]析构函数无法通过地址调用故手动输出cout

[0]

endl;//vprt[0]析构函数无法通过地址调用故手动输出cout

[0]

[4]Drive_multyBase::Drive_multyBaseI

*(int*)((int

菱形继承也称为钻石型继承或重复继承它指的是基类被某个派生类简单重复继承了多次。

这样派生类对象中拥有多份基类实例这会带来一些问题。

为了方便叙述我们不使用上面的代码了而重新写一个重复继承的继承层次

class

}};这时根据单继承我们可以分析出B1B2类继承于B类时的内存布局。

又根据一般多继承我们可以分析出D类的内存布局。

我们可以得出D类子对象的内存布局如下图

D类对象内存布局中图中青色表示b1类子对象实例黄色表示b2类子对象实例灰色表示D类子对象实例。

从图中可以看到由于D类间接继承了B类两次导致D类对象中含有两个B类的数据成员ib一个属于来源B1类一个来源B2类。

这样不仅增大了空间更重要的是引起了程序歧义

d;d.ib

//二义性错误,调用的是B1的ib还是B2的ibd.B1::ib

//正确d.B2::ib

//正确尽管我们可以通过明确指明调用路径以消除二义性但二义性的潜在性还没有消除我们可以通过虚继承来使D类只拥有一个ib实体。

6.虚继承

虚继承解决了菱形继承中最派生类拥有多个间接父类实例的情况。

虚继承的派生类的内存布局与普通继承很多不同主要体现在

虚继承的子类如果本身定义了新的虚函数则编译器为其生成一个虚函数指针vptr以及一张虚函数表。

该vptr位于对象内存最前面。

vs非虚继承直接扩展父类虚函数表。

虚继承的子类也单独保留了父类的vprt与虚函数表。

这部分内容接与子类内容以一个四字节的0来分界。

虚继承的子类对象中含有四字节的虚表指针偏移值。

6.1.虚基类表解析

在C对象模型中虚继承而来的子类会生成一个隐藏的虚基类指针vbptr在Microsoft

Visual

C中虚基类表指针总是在虚函数表指针之后因而对某个类实例来说如果它有虚基类指针那么虚基类指针可能在实例的0字节偏移处该类没有vptr时vbptr就处于类实例内存布局的最前面否则vptr处于类实例内存布局的最前面也可能在类实例的4字节偏移处。

一个类的虚基类指针指向的虚基类表与虚函数表一样虚基类表也由多个条目组成条目中存放的是偏移值。

第一个条目存放虚基类表指针vbptr所在地址到该类内存首地址的偏移值由第一段的分析我们知道这个偏移值为0类没有vptr或者-4类有虚函数此时有vptr。

我们通过一张图来更好地理解。

虚基类表的第二、第三…个条目依次为该类的最左虚继承父类、次左虚继承父类…的内存地址相对于虚基类表指针的偏移值这点我们在下面会验证。

6.2.简单虚继承

B根据我们前面对虚继承的派生类的内存布局的分析B1类的对象模型应该是这样的

int

这个结果与我们的C对象模型图完全符合。

这时我们可以来分析一下虚表指针的第二个条目值12的具体来源了回忆上文讲到的

第二、第三…个条目依次为该类的最左虚继承父类、次左虚继承父类…的内存地址相对于虚基类表指针的偏移值。

在我们的例子中也就是B类实例内存地址相对于vbptr的偏移值也即是[4]-[1]的偏移值结果即为12从地址上也可以计算出来007CFDFC-007CFDF4结果的十进制数正是12。

现在我们对虚基类表的构成应该有了一个更好的理解。

6.3.虚拟菱形继承

菱形虚拟继承下最派生类D类的对象模型又有不同的构成了。

在D类对象的内存构成上有以下几点

在D类对象内存中基类出现的顺序是先是B1最左父类然后是B2次左父类最后是B虚祖父类D类对象的数据成员id放在B类前面两部分数据依旧以0来分隔。

编译器没有为D类生成一个它自己的vptr而是覆盖并扩展了最左父类的虚基类表与简单继承的对象模型相同。

超类B的内容放到了D类对象内存布局的最后。

int

在C语言中“数据”和“处理数据的操作函数”是分开来声明的也就是说语言本身并没有支持“数据和函数”之间的关联性。

在C中我们通过类来将属性与操作绑定在一起称为ADT抽象数据结构。

C语言中使用struct结构体来封装数据使用函数来处理数据。

举个例子如果我们定义了一个struct

Point3如下

{printf((%f,%f,%f),pd-x,pd-y,pd_z);

}而在C中我们更倾向于定义一个Point3d类以ADT来实现上面的操作:

class

pt){os(pr.x(),pt.y(),pt.z());}看到这段代码很多人第一个疑问可能是加上了封装布局成本增加了多少答案是class

Point3d并没有增加成本。

学过了C对象模型我们知道Point3d类对象的内存中只有三个数据成员。

上面的类声明中三个数据成员直接内含在每一个Point3d对象中而成员函数虽然在类中声明却不出现在类对象object之中这些函数(non-inline)属于类而不属于类对象只会为类产生唯一的函数实例。

所以Point3d的封装并没有带来任何空间或执行期的效率影响。

而在下面这种情况下C的封装额外成本才会显示出来

虚函数机制virtual

虚继承关系产生虚基类用于在多重继承下保证基类在子类中拥有唯一实例。

不仅如此Point3d类数据成员的内存布局与c语言的结构体Point3d成员内存布局是相同的。

C中处在同一个访问标识符指public、private、protected下的声明的数据成员在内存中必定保证以其声明顺序出现。

而处于不同访问标识符声明下的成员则无此规定。

对于Point3类来说它的三个数据成员都处于private下在内存中一起声明顺序出现。

我们可以做下实验

void

a(1,2,3);TestPoint3Member(a);运行结果

总结一下

不考虑虚函数与虚继承当数据都在同一个访问标识符下C的类与C语言的结构体在对象大小和内存布局上是一致的C的封装并没有带来空间时间上的影响。

今有类如下

编译器为空类安插1字节的char以使该类对象在内存得以配置一个地址。

b1虚继承于b编译器为其安插一个4字节的虚基类表指针32为机器此时b1已不为空编译器不再为其安插1字节的char优化。

b2同理。

d含有来自b1与b2两个父类的两个虚基类表指针。

大小为8字节。



SEO优化服务概述

作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。

百度官方合作伙伴 白帽SEO技术 数据驱动优化 效果长期稳定

SEO优化核心服务

网站技术SEO

  • 网站结构优化 - 提升网站爬虫可访问性
  • 页面速度优化 - 缩短加载时间,提高用户体验
  • 移动端适配 - 确保移动设备友好性
  • HTTPS安全协议 - 提升网站安全性与信任度
  • 结构化数据标记 - 增强搜索结果显示效果

内容优化服务

  • 关键词研究与布局 - 精准定位目标关键词
  • 高质量内容创作 - 原创、专业、有价值的内容
  • Meta标签优化 - 提升点击率和相关性
  • 内容更新策略 - 保持网站内容新鲜度
  • 多媒体内容优化 - 图片、视频SEO优化

外链建设策略

  • 高质量外链获取 - 权威网站链接建设
  • 品牌提及监控 - 追踪品牌在线曝光
  • 行业目录提交 - 提升网站基础权威
  • 社交媒体整合 - 增强内容传播力
  • 链接质量分析 - 避免低质量链接风险

SEO服务方案对比

服务项目 基础套餐 标准套餐 高级定制
关键词优化数量 10-20个核心词 30-50个核心词+长尾词 80-150个全方位覆盖
内容优化 基础页面优化 全站内容优化+每月5篇原创 个性化内容策略+每月15篇原创
技术SEO 基本技术检查 全面技术优化+移动适配 深度技术重构+性能优化
外链建设 每月5-10条 每月20-30条高质量外链 每月50+条多渠道外链
数据报告 月度基础报告 双周详细报告+分析 每周深度报告+策略调整
效果保障 3-6个月见效 2-4个月见效 1-3个月快速见效

SEO优化实施流程

我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:

1

网站诊断分析

全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。

2

关键词策略制定

基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。

3

技术优化实施

解决网站技术问题,优化网站结构,提升页面速度和移动端体验。

4

内容优化建设

创作高质量原创内容,优化现有页面,建立内容更新机制。

5

外链建设推广

获取高质量外部链接,建立品牌在线影响力,提升网站权威度。

6

数据监控调整

持续监控排名、流量和转化数据,根据效果调整优化策略。

SEO优化常见问题

SEO优化一般需要多长时间才能看到效果?
SEO是一个渐进的过程,通常需要3-6个月才能看到明显效果。具体时间取决于网站现状、竞争程度和优化强度。我们的标准套餐一般在2-4个月内开始显现效果,高级定制方案可能在1-3个月内就能看到初步成果。
你们使用白帽SEO技术还是黑帽技术?
我们始终坚持使用白帽SEO技术,遵循搜索引擎的官方指南。我们的优化策略注重长期效果和可持续性,绝不使用任何可能导致网站被惩罚的违规手段。作为百度官方合作伙伴,我们承诺提供安全、合规的SEO服务。
SEO优化后效果能持续多久?
通过我们的白帽SEO策略获得的排名和流量具有长期稳定性。一旦网站达到理想排名,只需适当的维护和更新,效果可以持续数年。我们提供优化后维护服务,确保您的网站长期保持竞争优势。
你们提供SEO优化效果保障吗?
我们提供基于数据的SEO效果承诺。根据服务套餐不同,我们承诺在约定时间内将核心关键词优化到指定排名位置,或实现约定的自然流量增长目标。所有承诺都会在服务合同中明确约定,并提供详细的KPI衡量标准。

SEO优化效果数据

基于我们服务的客户数据统计,平均优化效果如下:

+85%
自然搜索流量提升
+120%
关键词排名数量
+60%
网站转化率提升
3-6月
平均见效周期

行业案例 - 制造业

  • 优化前:日均自然流量120,核心词无排名
  • 优化6个月后:日均自然流量950,15个核心词首页排名
  • 效果提升:流量增长692%,询盘量增加320%

行业案例 - 电商

  • 优化前:月均自然订单50单,转化率1.2%
  • 优化4个月后:月均自然订单210单,转化率2.8%
  • 效果提升:订单增长320%,转化率提升133%

行业案例 - 教育

  • 优化前:月均咨询量35个,主要依赖付费广告
  • 优化5个月后:月均咨询量180个,自然流量占比65%
  • 效果提升:咨询量增长414%,营销成本降低57%

为什么选择我们的SEO服务

专业团队

  • 10年以上SEO经验专家带队
  • 百度、Google认证工程师
  • 内容创作、技术开发、数据分析多领域团队
  • 持续培训保持技术领先

数据驱动

  • 自主研发SEO分析工具
  • 实时排名监控系统
  • 竞争对手深度分析
  • 效果可视化报告

透明合作

  • 清晰的服务内容和价格
  • 定期进展汇报和沟通
  • 效果数据实时可查
  • 灵活的合同条款

我们的SEO服务理念

我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。

提交需求或反馈

Demand feedback