《深度探索c++对象模型》读书笔记(一)

原创 2016年08月30日 15:51:57

本文以下内容为深度探索c++对象模型的笔记

深度探索c++对象模型是Stanley B Lippman的著作,对c++进行了较深层次的探讨。于我而言,这本书解答了我多年(半年)的疑惑:

虚函数是怎么实现的?

在此分享一二。由于博主本人水平十分有限,因此只能用简单的形式写出来。

虚函数只是c++从语法上支持的规则,概念上讲,任何编译器都可以有自己的实现,这些实现可以各不相同,只要在程序员的角度上看起来相同就可以。无论是哪一种实现,都要从c++的对象模型讲起。Lippman也提到了几种不同的对象模型。比如将类内所有成员都储存在外部,实际只有一个个slot存在于对象之中的简单对象模型;或者是在对象中储存两个指针分别指向数据成员表和成员函数表的表格驱动对象模型。
Lippman着重提到的,便是c++作者当初所设计并仍占有很大优势的对象模型:
非静态数据成员被放在每一个具体的对象之中
静态数据成员则被放在其它地方
静态成员函数和非静态成员函数也被放在其它地方
虚函数以两个步骤支持:
1、每个class产生一堆指向虚函数的指针,放在表格当中,这个表格被称为虚函数表(virtual table)vtbl
2、每个具体的对象之中安插一个指针,指向相关的vtbl。这个指针叫做vptr。vptr的设定和重置由每一个class的构造函数、析构函数、拷贝复制运算符自动完成。每     一个class所关联的type_info object也由vtbl指出,通常放在表格的第一个slot
那么接下来,我会一一解释我自己在阅读《c++ primer》虚函数相关部分时遇到的各种问题:
0、为什么同一个继承体系中的虚函数需要有相同的声明?
因为当编译器遇到一个函数调用时,名称检查是从对象的静态类型开始的。在继承体系中,对于虚函数的使用往往是应用其多态性,也就是通过基类指针或者引用经动态绑定使用派生类中的虚函数版本。由于编译期编译器无法知道一个指针或者引用实际绑定的对象是什么类型,所以才会默认检查其静态类型。既然如此,那么函数的调用形式就必须可以与基类中的某个函数相匹配。换句话说,你调用的所有函数在检查时是一样的,只不过这个函数恰好是虚函数而已。因此,派生类中的虚函数声明才需要基类中一致。
1、为什么虚函数操作要经由指针或者引用完成?
假如不是指针或引用,考虑其形式。类似于
//Derived是Base的派生类
Derived d;
Base b = d;
这时会发生切割现象。我们知道,在这种情况下,对b进行的虚函数调用会调用Base的版本。假设这个d是完完全全地切割并复制给b,那么d的虚函数表应该也会复制给b。那么为什么不会调用Derived的版本呢?当然是因为这并不是完完全全的复制。完完全全,Bitwise copy,即对对象中的每一个位都复制。前面我们说,c++中的拷贝构造函数或者是拷贝赋值运算符都会对vptr进行设定和重置操作。因此,在技术上,这种“现象”是合理的。

问题在于,首先,为什么这里会有一个拷贝函数,其次,为什么Base的拷贝构造函数需要将b的vptr重置成Base的呢,最后,指针或引用跟对象形式有什么根本的不同?

第一个问题需要补充,即在c++里,一个类如果没有定义自己的拷贝构造函数,编译器并不总是会合成一个拷贝构造函数,相反,编译器会直接对新的对象进行逐位复制。有没有一个合成的拷贝构造函数的差别并不是让人很清楚。毕竟拷贝构造函数完成复制的概念深入人心。然而拷贝构造函数的意义就是能够不让类的拷贝出错,因此在拷贝不会出错时,编译器是可以不合成拷贝函数的。(这里讨论的都是没有自定义的拷贝构造函数的时候)
那么第一个问题的答案是:
因为这里有一个虚函数(题设啊题设)。当:
class中含有一个member object而后者有一个拷贝构造函数时(无论是自定义的还是合成的)、class继承自一个拥有拷贝构造函数的基类时、class声明了一个或者多 个虚函数时、class的继承链中有一个或多个虚基类时,编译器才会合成一个拷贝构造函数。
第二个问题的补充是,有权利并不代表一定得这么做。编译器为什么要这么做呢?
虚函数首先要满足多态性,而作为成员函数,派生类中覆盖的虚函数很有可能使用了派生类自己定义的成员。而切割并复制的过程,则是将那些新的成员统统丢弃掉了。使用那些成员将是没有意义的。于是,编译器将vptr重置成了静态类型。
所以第三个问题也就迎刃而解了。无论是指针还是引用,都不会损失对象本身的内存布局。毕竟“指针都是一样的,类型的差别仅仅是解释方式的不同”,而c++的语法设计,又使得基类到派生类的指针能够正确地被使用。

很有趣的一点是,编译器实现,语法,规则。这些什么是因什么是果呢?

第一次写博客好费事啊,就先写到这里吧。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

《深度探索C++对象模型》读书笔记——关于对象【for_wind】

//整理之,分享之,欢迎指正。for_wind 1、C与C++的区别:       概括来说,C程序中程序性地使用全局数据[注1]。而C++采用ADT(abstract data tpye)或...

《深度探索C++对象模型》读书笔记

Inside The C++ Object Model Stanley B. Lippman   转:http://blog.csdn.net/dwater 《深度探索C++对象模型》读书笔记    ...

《深度探索C++对象模型》读书笔记(1)

《深度探索C++对象模型》读书笔记(1)。   在C++中,有两种class data members:static和nonstatic,以及三种class member functi...

《深度探索C++对象模型》读书笔记

1,  在Stroustrup当初设计的C++对象模型中,Nonstatic data members被配置于每一个class object之内,static data members则被存放在所...

《深度探索C++对象模型》读书笔记——Function 语意学【for_wind】

Member的各种调用方式

【读书笔记】深度探索C++对象模型(更新中

第三章 Data 语义学先看栗子下面通过几个例子大概展示了C++对象内存布局在复杂的继承关系中使用的策略。 开始之前,先来读读下面这段话 C++ Standard 并不强制规定如 “base c...

《深度探索C++对象模型》读书笔记(2)。

default constructor仅在编译器需要它时,才会被合成出来。    通常来说,由编译器合成出来的default constructor是没啥用的(trivial),但有以下几种例外...

深度探索c++对象模型读书笔记(一)

深度探索c++对象模型读书笔记(一)1.c++有两种class data menmbers:static 和 nonstatic 三种class menmber functions:static ...

深度探索C++对象模型读书笔记——2.3 程序转化语意学

C++

《深度探索C++对象模型》读书笔记——Data语意学【for_wind】

//整理之,分享之,欢迎指正。for_wind 1、类的实际大小。       每一个class object必须有足够的大小以容纳它所有的nonstatic data members。除了其...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)