内存布局: 虚继承 ---Empty virtual base classs (空虚基类)

2024-04-28 02:32

本文主要是介绍内存布局: 虚继承 ---Empty virtual base classs (空虚基类),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上一次只是给出一个代码 http://blog.csdn.net/OpenHero/archive/2006/04/02/648005.aspx,和运行过程中的内存位置,现在具体的按照步骤分析一下C++中虚拟继承中的情况,
  由于C++ stand(C++ 标准委员会) 并没有规定具体实现的办法,各个编译器厂商可能都采用不同的实现方法,
  或许在不同的年代使用的方法也不相同,有可能就会得到不同的结果,这里,我采用VC7.1作为调试工具,由潜
  入深的一步步分析C++ 虚继承中的内存布局;
  这一部分由Empty virtual base calss (空虚基类:空的calss)入手,查看VC7.1中是如何实现虚拟继承的.
  下面给出测试的代码:
  一,菱形的继承模式
  class A
  { };
  class B : virtual public A
  {
  };
  class C : virtual public A
  {
  };
  class D : public C, public B
  {
  };
  int _tmain(int argc, _TCHAR* argv[])
  {
  A a;
  B b;
  C c;
  D d;
  int *pd = (int*)&d;
  int *pd_c = (int*)(C*)(&d);
  int *pd_b = (int*)(B*)(&d);
  int *pd_a = (int*)(A*)(&d);
  cout<< sizeof(a) << endl;
  cout<< sizeof(b) << endl;
  cout<< sizeof(c) << endl;
  cout<< sizeof(d) << endl;
  cout<< pd << endl;
  cout<< pd_c << endl;
  cout<< pd_b << endl;
  cout<< pd_a << endl;
  return 0;
  }
  有人会问,
  class A
  { };
  A a, 其中的a会有大小嘛?当然,a里面什么都没有,姑且就认为它是空的吧,但是,编译器如何才能记住a,而又
  是空?你能想到一种实现的方法嘛?所以,编译器为了记住空的a,在a的内部放入了一个char的标记,作为它的
  唯一标识,这样编译器才能认识那个a. 如果你有兴趣,你可以尝试定义A a,b; 然后你看看a和b是否是一
  样的?现阶段来说,应该是不一样的,或许以后谁会发明新的实现方式,那个时候或许是一样也有可能了。
  现在让我们看看上面程序的输出情况,如下:
  1
  4
  4
  8
  0012FEAC
  0012FEAC
  0012FEB0
  0012FEB4
  这里我先不对上面的输出做出解释,或许你看了这样的输出会有疑惑,是不是和你想象的不一样?带着疑问
  继续往下面看。
  下面我再给出两段代码,同时给出他们各自的输出,然后再对这些输出做出解释:
  二,六边形(菱形一)
  class A
  { };
  class B : virtual public A
  {
  };
  class C : virtual public A
  {
  };
  class D : /*virtual */public B
  {
  };
  class E : /*virtual */public C
  {
  };
  class F : public E, public D
  {
  };
  int _tmain(int argc, _TCHAR* argv[])
  {
  A a;
  B b;
  C c;
  D d;
  E e;
  F f;
  int *pf = (int*)&f;
  int *pf_e = (int*)(E*)(&f);
  int *pf_d = (int*)(D*)(&f);
  int *pf_c = (int*)(C*)(&f);
  int *pf_b = (int*)(B*)(&f);
  int *pf_a = (int*)(A*)(&f);
  cout<< sizeof(a) << endl;
  cout<< sizeof(b) << endl;
  cout<< sizeof(c) << endl;
  cout<< sizeof(d) << endl;
  cout<< sizeof(f) << endl;
  cout<< pf << endl;
  cout<< pf_e << endl;
  cout<< pf_d << endl;
  cout<< pf_c << endl;
  cout<< pf_b << endl;
  cout<< pf_a << endl;
  return 0;
  }
  输出结果:
  1
  4
  4
  4
  4
  8
  0012FE94
  0012FE94
  0012FE98
  0012FE94
  0012FE98
  0012FE9C
  三,六边形(菱形二)
  class A
  { };
  class B : virtual public A
  {
  };
  class C : virtual public A
  {
  };
  class D : virtual public B
  {
  };
  class E : virtual public C
  {
  };
  class F : public E, public D
  {
  };
  int _tmain(int argc, _TCHAR* argv[])
  {
  A a;
  B b;
  C c;
  D d;
  E e;
  F f;
  int *pf = (int*)&f;
  int *pf_e = (int*)(E*)(&f);
  int *pf_d = (int*)(D*)(&f);
  int *pf_c = (int*)(C*)(&f);
  int *pf_b = (int*)(B*)(&f);
  int *pf_a = (int*)(A*)(&f);
  cout<< sizeof(a) << endl;
  cout<< sizeof(b) << endl;
  cout<< sizeof(c) << endl;
  cout<< sizeof(d) << endl;
  cout<< sizeof(e) << endl;
  cout<< sizeof(f) << endl;
  cout<< pf << endl;
  cout<< pf_e << endl;
  cout<< pf_d << endl;
  cout<< pf_c << endl;
  cout<< pf_b << endl;
  cout<< pf_a << endl;
  return 0;
  }
  输出结果:
  1
  4
  4
  8
  8
  16
  0012FE84
  0012FE84
  0012FE88
  0012FE8C
  0012FE90
  0012FE8C
  下面对其中的一,二做分析,三是一个有趣的现象,你可以考虑考虑:)
  可以看到,只要有virtual的地方,都多出4个字节,用来干嘛?
  在VC的编译器中,采用了virtual base calss pointer的方式来实现虚继承,从而保证子类回溯到基类的时
  候只存在一个实体。多出来的4个字节就是用来存放各自的指向virtual base class 的地址的。
  你可能又要问,a的大小为1,b,c……的大小只是4个4个增加,考虑内存对其,那不是会更大吗?
  看到前面提到的一个问题了吗?
  编译器只是为了记住a的时候才把a标示出来,如果知道a是空的,它既然有了子类,子类表示出来的时候,
  当然基类就存在其中了:)
  你可能又问? 那为什么不把子类也表示为1,不就行了吗?当然不行,就像上面提到的,因为是为了实现虚
  继承的菱形一样的继承样式,在子类中只存在基类一个实体,所以,就才用了指针的方式,而不是把整个实
  体放到子类的内存中。

这篇关于内存布局: 虚继承 ---Empty virtual base classs (空虚基类)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/942115

相关文章

html5的响应式布局的方法示例详解

《html5的响应式布局的方法示例详解》:本文主要介绍了HTML5中使用媒体查询和Flexbox进行响应式布局的方法,简要介绍了CSSGrid布局的基础知识和如何实现自动换行的网格布局,详细内容请阅读本文,希望能对你有所帮助... 一 使用媒体查询响应式布局        使用的参数@media这是常用的

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

CSS3 最强二维布局系统之Grid 网格布局

《CSS3最强二维布局系统之Grid网格布局》CS3的Grid网格布局是目前最强的二维布局系统,可以同时对列和行进行处理,将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局,本文介... 深入学习 css3 目前最强大的布局系统 Grid 网格布局Grid 网格布局的基本认识Grid 网

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re