欢迎来到天天文库
浏览记录
ID:46955999
大小:24.08 KB
页数:5页
时间:2019-12-01
《C++对象的数据成员》由会员上传分享,免费在线阅读,更多相关内容在行业资料-天天文库。
1、....C++对象的数据成员数据成员的布局对于一个类来说它的对象中只存放非静态的数据成员,但是除此之外,编译器为了实现virtual功能还会合成一些其它成员插入到对象中。我们来看看这些成员的布局。C++标准的规定·在同一个AccessSection(也就是private,public,protected片段)中,要求较晚出现的数据成员处在较大的内存中。这意味着同一个片段中的数据成员并不需要紧密相连,编译器所做的成员对齐就是一个例子。·允许编译器将多个AcessSection的顺序自由排列,而不必在乎它们的声明次序。但似乎没有编译器这样做。·对于继承类,C++标准并未指定是其基类成员在前还是自己
2、的成员在前。·对于虚基类成员也是同样的未予规定。一般的编译器怎么做?·同一个AccessSection中的数据成员按期声明顺序,依次排列。但成员与成员之间因为内存对齐的原因可能存在空当。·多个AccessSection按其声明顺序排放。·基类的数据成员总放在自己的数据成员之前,但虚基类除外。编译器合成的成员放在哪?为了实现虚函数和虚拟继承两个功能,编译器一般会合成Vptr和Vbptr两个指针。那么这两个指针应该放在什么位置?C++标准肯定是不曾规定的,因为它甚至并没有规定如何来实现这两个功能,因此就语言层面来看是不存在这两个指针的。对于Vptr来说有的编译器将它放在末尾,如Lippman领导开
3、发的Cfront。有的则将其放在最前面,如MS的VC,但似乎没人将它放在中间。为什么不放在中间?没有理由可以让人这么做,放在末尾,可以保持C++类对C的struct的良好兼容性,放在最前可以给多重继承下的指针或引用调用虚函数带来好处。看一小段代码:classX{学习参考....public:inta;virtualvoidvfc(){};};intmain(){usingnamespacestd;Xx;cout<<&x.a<<""<<&x<4、虚。对于Vbptr来说,有好几种方法,在这儿我们只看看VC的实现原理:对于由虚拟继承而得的类,VC会在其每一个对象中插入一个Vbptr,这个Vbptr指向vitualbaseclasstable(我称之为虚基类表)。虚基类表中则存放有其虚基类子对象相对于虚基类指针的偏移量。例如声明如classY:virtualpublicX的类的virtualbaseclasstable的虚基类表中当存储有X对象相对于Vbptr的偏移量。对象成员或基类对象成员后面的填充空白不能为其它成员所用看一段代码:classX{public:intx;学习参考....charc;};classX2:publicX{pub5、lic:charc2;};X2的布局应当是x(4),c(1),c2(1),这么说来sizeof(X2)的值应该是8?错了,实际上是12。原因在于X后面的三个字节的填充空白不能为c2所用。也就是说X2的大小实际上为:X(8)+c2(1)+填补(3)=12。这样看来编译器似乎是那么的呆板,其实不然,看一下下面的语句会发生什么?X2x2;Xx;x2=x;如果X后面的填充空白可以被c2使用的话,那么X2和X都将是8字节。上面的语句执行后x2.c2的值会是多少?一个不确定的值!这样的结果肯定不是我们想要的。Vptr与Vbptr1·在多继承情况下,即使是多虚拟继承,继承而得的类只需维护一个Vbptr;而多6、继承情况下Vptr则可能有要维护多个Vptr,视其基类有几个有虚函数。·一条继承线路只有一个Vptr,但可能有多个Vbptr,视有几次虚拟继承而定。换言之,对于一个继承类对象来说,不需要新合成vptr,而是使用其基类子对象的vptr。而对于一个虚拟继承类来说,必须新合成一个自己的Vbptr。如:classX{virtualvoidvf(){};学习参考....};classX2:virtualpublicX{virtualvoidvf(){};};classX3:virtualpublicX2{virtualvoidvf(){};}X3将包含有一个Vptr,两个Vbptr。确切的说这两个Vbp7、tr一个属于X3,一个属于X3的子对象X2,X3通过其Vbptr找到子对象X2,而X2通过其Vbptr找到X。其中差别在于vptr通过一个虚函数表可以确切地知道要调用的函数,而Vbptr通过虚基类表只能够知道其虚基类子对象的偏移量。这两条规则是由虚函数与虚拟继承的实现方式,以及受它们的存取方式和复制控制的要求决定的。数据成员的存取静态数据成员相当于一个仅对该类可见的全局变量,因为程序中只存在一个静
4、虚。对于Vbptr来说,有好几种方法,在这儿我们只看看VC的实现原理:对于由虚拟继承而得的类,VC会在其每一个对象中插入一个Vbptr,这个Vbptr指向vitualbaseclasstable(我称之为虚基类表)。虚基类表中则存放有其虚基类子对象相对于虚基类指针的偏移量。例如声明如classY:virtualpublicX的类的virtualbaseclasstable的虚基类表中当存储有X对象相对于Vbptr的偏移量。对象成员或基类对象成员后面的填充空白不能为其它成员所用看一段代码:classX{public:intx;学习参考....charc;};classX2:publicX{pub
5、lic:charc2;};X2的布局应当是x(4),c(1),c2(1),这么说来sizeof(X2)的值应该是8?错了,实际上是12。原因在于X后面的三个字节的填充空白不能为c2所用。也就是说X2的大小实际上为:X(8)+c2(1)+填补(3)=12。这样看来编译器似乎是那么的呆板,其实不然,看一下下面的语句会发生什么?X2x2;Xx;x2=x;如果X后面的填充空白可以被c2使用的话,那么X2和X都将是8字节。上面的语句执行后x2.c2的值会是多少?一个不确定的值!这样的结果肯定不是我们想要的。Vptr与Vbptr1·在多继承情况下,即使是多虚拟继承,继承而得的类只需维护一个Vbptr;而多
6、继承情况下Vptr则可能有要维护多个Vptr,视其基类有几个有虚函数。·一条继承线路只有一个Vptr,但可能有多个Vbptr,视有几次虚拟继承而定。换言之,对于一个继承类对象来说,不需要新合成vptr,而是使用其基类子对象的vptr。而对于一个虚拟继承类来说,必须新合成一个自己的Vbptr。如:classX{virtualvoidvf(){};学习参考....};classX2:virtualpublicX{virtualvoidvf(){};};classX3:virtualpublicX2{virtualvoidvf(){};}X3将包含有一个Vptr,两个Vbptr。确切的说这两个Vbp
7、tr一个属于X3,一个属于X3的子对象X2,X3通过其Vbptr找到子对象X2,而X2通过其Vbptr找到X。其中差别在于vptr通过一个虚函数表可以确切地知道要调用的函数,而Vbptr通过虚基类表只能够知道其虚基类子对象的偏移量。这两条规则是由虚函数与虚拟继承的实现方式,以及受它们的存取方式和复制控制的要求决定的。数据成员的存取静态数据成员相当于一个仅对该类可见的全局变量,因为程序中只存在一个静
此文档下载收益归作者所有