[读书系列] 深度探索C++对象模型 初读
2012年底~2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起《深度探索C++对象模型》这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio之类的工具的话,写C++代码感觉要比较费劲,最近重读了这本书后,感觉要写点东西下来(因为懒得用笔作笔记,太慢了-_-!)加深下印象。
以前还是新手的时候,总是认为:
1.class如果没有定义任何constructor的话,编译器会自动合成一个default constructor(我习惯叫缺省的构造函数)出来
2.编译器合成出来的default constructor会为“class内的每一个data member“作初始化
现在开始往不为新手所知的编译器合成的default constructor究竟做了什么神秘的事这个方向飞吧!:)
《深度探索C++对象模型》说道编译器会在需要的时候自动合成default constructor,而“在需要的时候”指的有这样4种情况:
a1.class中带有member class object
a2.class的base class 带有default constructor
a3.class拥有(继承)一个或者多个virtual function
a4.带有一个virtual base class
a1的情况:
class A { public: A() {} }; class B : A { private: A a; int count; }; void Test() { B b;//B::a必须在初始化 } //此时编译器会为class B 自动生成一个default constructor,然后其中负责了class A 对象a的初始化工作 //生成的构造函数伪代码可能像下面这样: class B { public: B() { //编译器生成的,且这段代码是在用户代码之前的 this.a.A::A(); //但是count是class B 对编译器来说不是必要的(对用户来说是必要的),所以count的初始化工作时是需要用户自己负责的 //如this.count=0; } };
当然,如果class B中组合有多个其他的class,且它们都像class A一样带有default constructor,那么编译器会以class B中声明它们的顺序,依序自动生成每个class对应的default constructor,比如:
class B {public: B() {}}; class C {public: C() {}}; class D {private: A a; B b; C c;}; void Test() { D d;//会在class D中自动生成default constructor } //如下自动生成的default constructor伪代码 class D { public: D() { a.A::A(); b.B::B(); c.C::C(); } };
a2的情况:和1的类似,编译会为所有带default constructor的class 的derived class 生成需要的default constructor,用于初始化base class,当然这些代码会安插在用户初始化自己的data member的代码之前!!如果同时存在带default constructor的 member class objects,那么会在上述的初始化base class的default constructor之后生成
a3.的情况:
//直接摘抄《深度探索C++对象模型》的示例 class Widget { public: ; }; void flip(const Widget& widget) { widget.flip(); } //假设Bell 和Whistle都派生自Widget void foo() { Bell b; Whistle w; flip(b); flip(w); }
在这种情况下,在编译期间,会有这样的行为:
1.一个virtual funciton table被生成出来,里面存放着虚函数的地址(当然还有type_info类型信息);
2.为每个class object合成一个vptr(virtual pointer,指向虚表的指针),期内存放着相关class的vtbl(上述的virtual function table)的地址(ps:相关class可指的是base class中的虚表)
因此上述的widget.flip()的调用可能是这样的决议:(*widget.vptr[1])(&widget), (ps:这里假定Widget只有1个虚函数,vptr[1]指的就是存放指向虚函数flip()的地址,而vptr[0]指的是类型信息), &widget指的是Widget类的某一个实例交付给flip的this指针。
为了使多态这个机制发挥它应有的作用,编译器会为Widget的每个object(或者其派生类object)合成vptr,存放适当的指向vtbl(virtual function table)的地址,因此对于那些没有声明任何constructor的class,编译器这个时候就为它们自动生成了defalut constructor来做vptr做初始化的工作!
a4的情况:
//摘自《深度探索C++对象模型》 class X { public: int i; }; class A : public virtual X { public: int j; }; class B : public virtual X { public: int k; }; class C : public A, public B { public: int d; } ; } mian() { foo(new A); foo(new B); }
继承关系如右图:
C中的内存布局如右图:
编译器无法在编译期决议出pa->X::i的位置,因为pa的类型是可变的,可能是A或C或X类型,因此也就无法确切的知道X::i在内存中的偏移位置,这时编译器对该行代码可能的转变操作有
pa->__pacX->i = 1024,而这个__pacX正是编译器安插的指向base class的指针,在运行期才可正确决议如何存取X::i,这个操作在构造期间完成,如果用户没有声明任何constructor,那么这时编译器会合成default constructor来完成这个操作,安插对base calss进行存取操作的代码!
至此看回前面的两个观点:
1.任何class如果没有定义default constructor(我习惯叫默认的构造函数)的话,编译器会自动合成一个出来
2.编译器合成出来的default constructor会为“class内的每一个data member“作初始化
没有1个是正确的!-_-!!!当然你可以说本来就是这样,但相信很多新手肯定和我最开始的时候一样抱着这样的想法!!!
下面 补上C++对象模型的结构图:来自网络 ,顺便大致说说:
图中对应的代码大致如下:
class Point { public: Point(float xval) { this.x = xval; } virtual ~Point(){ //... } float x() const { return this._x; } static int PointCount(){ //... ; } protected: virtual ostream& print(ostream&) const { //... } float _x; static int _point_count; };
大致的理解如下:
1.编译器为每个Point类对象pt安插一个虚表指针_vptr_Point,指向虚表Virtual table for Point
2.上述虚表中0索引位置存放type_info类型信息地址,1索引位置存放虚析构函数地址,2索引位置存放虚函数print地址
3.nonstatic data members像虚表指针一样,存在于每个class object之内(亦即类的每个实例都有着自己的数据)
4.staitc data members 和 nonstatic或者static function members 存在于class object之外
基于上述说明,现在我不会再像以前迷惑于为什么对有些类(比如内含有虚函数)取sizeof时得到的结果会大于自己的猜想,因为类的vptr虚表指针也需要占据内存(32位机器占用
4个字节),如果是属于继承链中的一环,那还要算上其base class中的vptr开销!!
再进一步的,可以深入了解对象的(数据)内存布局了!!希望我们不会再为constuctor背后做的事所迷惘,冲过这令人眩晕的“暗涌”吧!!!
[读书系列] 深度探索C++对象模型 初读的更多相关文章
- 《深度探索C++对象模型》读书笔记(一)
前言 今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++.操作系统.计算机网络.算法和数据结构等.C++就先从这本<深度探索C++对象模型>开始.不同于& ...
- 读书笔记《深度探索c++对象模型》 概述
<深度探索c++对象模型>这本书是我工作一段时间后想更深入了解C++的底层实现知识,如内存布局.模型.内存大小.继承.虚函数表等而阅读的:此外在很多面试或者工作中,对底层的知识的足够了解也 ...
- 柔性数组-读《深度探索C++对象模型》有感 (转载)
最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...
- 柔性数组-读《深度探索C++对象模型》有感
最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...
- 深度探索C++对象模型
深度探索C++对象模型 什么是C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各个支持的底层实现机制. 抽象性与实际性之间找出平衡点, 需要知识, 经验以及许多思考. 导读 这本书是C+ ...
- 拾遗与填坑《深度探索C++对象模型》3.3节
<深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...
- 拾遗与填坑《深度探索C++对象模型》3.2节
<深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...
- c++学习书籍推荐《深度探索C++对象模型》下载
百度云及其他网盘下载地址:点我 百度云及其他网盘下载地址:点我 编辑推荐 如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么这本<深度探索C++对象模型>正适合你 作者简介 ...
- [转]《深度探索C++对象模型》读书笔记[一]
前 言 Stanley B.Lippman1. 任何对象模型都需要的三种转换风味: ü 与编译器息息相关的转换 ü 语言语义转换 ü 程序代码和对象模型的 ...
随机推荐
- 怎么应用vertical-align,才能生效?
vertical-align 的使用 以前总是想要一些元素垂直居中对齐,经常用line-height,可是对于图片来说,line-height的表现并不理想(非常不理想)可看我的文章:line-he ...
- 纯css实现轮播图
轮播图的实现原理其实是比较简单的 举个例子 <div class="main"> <div class="div-main"></ ...
- vue.js学习第一节
<div id="app" class="app"> <p>{{ message }}</p> <p>{{ in ...
- C# 6 与 .NET Core 1.0 高级编程 - 37 章 ADO.NET
译文,个人原创,转载请注明出处,有不对的地方欢迎指出与交流. 英文原文:Professional C# 6 and .NET Core 1.0 - 37 ADO.NET --------------- ...
- GitLab Wiki 内容恢复版本管理
原来一直在网站上写Wiki文档, 最近手欠误删一篇文档, 想要恢复文档时才发现原来gitlab的Wiki是用git管理的从此再也不用为误删担心了 实现步骤: mac系统安装gollow brew in ...
- JAVA文件名命名规范
JAVA语言,有严格的大小写区分要示. JAVA源文件名必须符合以下规则: 1.必须以.java结尾.这样才能被编辑器javac.exe所编辑. 2.源文件中如果只有一个类,文件名必须与该类名相同. ...
- C#剪贴板对文件的复制、粘贴操作
1.把文件加到剪贴板: System.Collections.Specialied.StringCollection files=new System.Collections.Specialied.S ...
- 读书笔记 effective c++ Item 2 尽量使用const,枚举(enums),内联(inlines),不要使用宏定义(define)
这个条目叫做,尽量使用编译器而不要使用预处理器更好.#define并没有当作语言本身的一部分. 例如下面的例子: #define ASPECT_RATIO 1.653 符号名称永远不会被编译器看到.它 ...
- JAVA中浮点数显示
//Java小数点后留两位 double num1 =11; double num2 =21; String num3 =""; //函数可以确定十进制数的显示格式:百分数,限定小 ...
- PHPCMS-后台管理中心
这个就是便捷管理网页,可以通过这个后台进行修改.增删一些东西,还可以利用一些网页模板来建立网页 首先就是下载好这个后台管理中心,这个从网上下载就好了,记住这个要安装在WampServer中的www文件 ...