您的位置:首页 > 编程语言 > C语言/C++

读深入C++对象模型的总结

2016-04-07 13:31 330 查看
这个博客简直就是个坑啊。。。没想到被我扔了这么久了

这本书也是个坑,我一年前就翻过这本书,看了个开头头就开始晕晕的了,时隔一年,我终于填了这个坑。

这本书确实是进阶类型的书吧,没有一定的基础看起来会很头疼,不知道在讲什么,建议想上手的同学想打好基础,切勿急躁。

那就说一下这本书,这本书基本都是在讲C++在对象模型上的取舍与设计问题,可以看到C++中的很多缺点其实是不可避免的,其难于学习的代价基本都是以提高效率换来的。其中只有执行期语义学一章对普遍比较关心的new操作符进行了说明,关于C的东西基本没有提,可能都当做是基础了。

可以说这本书最精华的地方是前面的几章多一点,后面牵扯到很多设计的细节问题。那些细节问题在此阐述实在没有什么意义。因此我只谈前面几章中的哲学语义。

class这种东西实际上就是struct的扩充,可以说是为了能让C语言表现出面向对象的特征才有了C艹,也因而,C++中的对象内存布局其还是C中的那一套,除了可能会有的额外添加的指针附加,其布局还是采用的二进制布局,所以它几乎没有损耗C中性能问题,可以参见第一章中提出的各类模型,这个模型是最快的。除此之外,作者还提到了class与struct的区别问题(当初我就是被这个问题给挡住了,囧),说实话这就是一个哲学问题,我们完全可以将这个问题忽略不计,作者的探讨我总结为大概就是两点:1是风格问题,2是class引入了一种全新的哲学语意,因此引入是有必要的。嗯,就是这么简单。大概读完第一章你就会发现你要开始以一种更高的层面来开始解读这门语言,来探讨这门语言的语意哲学与设计,这真是一种奇妙的感觉。最后作者提出了几种编程风格,这几种编程风格最好不要太多的掺杂在一起,而是能分开就分开,不然可能连语意都不正确。

然后是构造函数,在读这本书以前,我从未知道C++standard原来要这么看的。。。构造函数以及析构函数都是在需要的时候被产生出来,单纯的看这句话我决想不到其中所包含的意义,它其实意味着所谓的默认构造函数,在很多情况下,其实根本就不存在!这也是为了效率所做出的设计,这样在纯C风格的代码情况下,你的代码不会有任何其他的负担,就像原来它在C中应有的样子一样。仅在用到了非默认构造函数或者virtual函数时它才会变成有需要的而被产生出来。对了,还有一点有必要提一下,只要在允许的情况下,C++会把拷贝构造函数变成bitwise
copy,这绝对比你自己写一个拷贝构造要快,并且还麻烦,因此在这种情况下倒是不写为妙。那么什么时候会不允许bitwise呢?总结来说还是那两种情况:1你的class中出现过自己定义的copy constructor,2是你的class 中出现了virtual关键字。是不是好像刚刚已经看过这段话了,是的,在这种constructor的设计中,C++有着高度的统一性,你不需要背,只要去理解它的哲学语义就可以了,一切甚至可以从那里倒推出来。

第三章讲述了对象中的各种布局问题,第四章讲述了class中的函数语意。我觉得这两章可以一起简要的总结,其实第四章中重点讲述的virtual函数构造函数实际就是data中那个vptr的编译器构造指针的重点详解。其实只要你的C基础够好,看这两章是很轻松的,data布局还是C中的那一套,只不过增加了继承的情况和一个(或几个)vptr的情况,不居中该align的align,有意思的是有了accession这个概念以后C++并没有规定它们之间的顺序关系,当然现实中的编译器布局实现都是顺序放置的,都是符合人的直觉的。主要需要你注意的就是多继承和virtual
base这种情况,这东西特别恶心,把本来看起来无比完美的单继承设计给搞的一团糟,逼你需要各种hack方式,这本书本身甚至都没能讲述全面,很多东西仍然超出了本书的讲解范围。首先你需要各种调整偏移量,包括指针偏移等等,这是最基本的应对多继承的方法,其实多继承就够喝一壶的了,virtual 的作用大概就是在多继承的基础上加了指针,并以指针作出调整,也因为指针的存在而增多了间接性与复杂性,你可以想像一下当你把一个derived对象赋值给一个virtual base 指针的时候,你要怎么通过base地址调整至原来derived的对象地址?根据对象布局似乎thunk可以做这件事,那么如果一个virtual
base class 从另外一个virtual base class继承而来,并且都有virtual function的话那该怎么办呢,简单来说还是指针,尽管作者没有说,我觉得可以大概推一下,首先derived class维护了指向这两个virtual base class的指针,减少间接性,然后是virtual table,你必须在base class中维护两份table,每一份中都有完整的virtual function slots,但是不同的是thunk中带的偏移量不同,实现的难点就在于偏移量的计算问题,比如从某个base层中去调用derived的成员,你该如何调整,只能针对每一层的base都提前计算好偏移值,这意味着大量编译器以及执行期的运算量。
友情提示:如果你用了它,你的程序效率会被严重拖累,因此作者甚至建议使用空的virtual base,在函数中也是一样,麻烦的都是这个virtual base,具有高度复杂性,甚至由于它的存在还要回去调整原来的设计,比如说virtual 函数,两个virtual碰撞在一起真是发生了无比璀璨的火花,thunk以及其他类都出来辅助这一过程,记录各种各样的偏移,这酸爽。。有些东西至今我再翻的时候还是有点迷迷糊糊,只能多在坑里呆一会再爬出去了。尽管理论上可以做出的某些优化,实际中,编译器无一例外全是臣妾万万做不到啊。。。。。其实除了virtual
这种东西,很多能在编译器决定的东西C++基本都帮你决定好了,我想这也是C++能这么快的原因之一。

最后在强调一遍,能在编译期做的事情绝不放在执行期。

C++是一门博大精深的语言,这坑大到让我看不到边际,并且随着C++需求与热度的不断减少,以后究竟还有多大学习C++的必要尚未可知,但可以确定的是OJ上应该是不会换的。要是想快点赚钱,我看还是去搞java吧。C++越来越吃力不讨好了感觉,唉,真不知道这世道是前进了还是后退了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 对象模型 理解