Inside the C++ Object Model 读书笔记(三)

本文详细探讨了C++中数据成员的绑定、布局、访问方式以及继承对其的影响。静态数据成员存储在程序的数据段,而访问非静态数据成员需要结合对象地址和成员偏移。虚拟继承导致类布局复杂,包括不变区域和共享区域,增加了访问的间接性和对象开销。指针到数据成员的使用不受继承影响,但虚拟继承可能降低优化效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Chapter 3. The Semantics of Data

class X {}; 
class Y : public virtual X {}; 
class Z : public virtual X {}; 
class A : public Y, public Z {};

sizeof X yielded 1 
sizeof Y yielded 8 
sizeof Z yielded 8 
sizeof A yielded 12 

3.1 The Binding of a Data Member

3.2 Data Member Layout

The static data members are stored in the program’s data segment independent of individual class objects.
The Standard allows the compiler the freedom to order the data members within multiple access sections within a class in whatever order it sees fit

template< class class_type, 
          class data_type1, 
          class data_type2 > 
char* 
access_order( 
   data_type1 class_type::*mem1, 
   data_type2 class_type::*mem2 ) 
{ 
   assert ( mem1 != mem2 ); 
   return 
      mem1 < mem2 
         ? "member 1 occurs first" 
         : "member 2 occurs first"; 
}

3.3 Access of a Data Member

Access of a nonstatic data member requires the addition of the beginning address of the class object with the offset location of the data member.

origin.x = 0.0; 
pt->x = 0.0; 

significantly different when accessed through the object origin or the pointer pt? The answer is the access is significantly different when the Point3d class is a derived class containing a virtual base class within its inheritance hierarchy and the member being accessed, such as x, is an inherited member of that virtual base class.

3.4 Inheritance and the Data Member

The actual ordering of the derived and base class parts is left unspecified by the Standard. In practice, the base class members always appear first, except in the case of a virtual base class. (In general, the handling of a virtual base class is an exception to all generalities, even, of course, this one.)

(Note: The figure shows the vptr placement at the end of the base class.)
image

Multiple Inheritance

image

Virtual Inheritance
A class containing one or more virtual base class subobjects, such as istream, is divided into two regions: an invariant region and a shared region. Data within the invariant region remains at a fixed offset from the start of the object regardless of subsequent derivations. So members within the invariant region can be accessed directly.

class Point2d { 
public: 
   ... 
protected: 
   float _x, _y; 
}; 

class Vertex : public virtual Point2d { 
public: 
   ... 
protected: 
   Vertex *next; 
}; 

class Point3d : public virtual Point2d { 
public: 
   ... 
protected: 
   float _z; 
}; 
class Vertex3d : 
   public Point3d, public Vertex { 
public: 
   ... 
protected: 
   float mumble; 
}; 
void 
Point3d:: 
operator+=( const Point3d &rhs ) 
{ 
   _x += rhs._x; 
   _y += rhs._y; 
   _z += rhs._z; 
}; 
under the cfront strategy, this is transformed internally into

// Pseudo C++ Code 
__vbcPoint2d->_x += rhs.__vbcPoint2d->_x; 
__vbcPoint2d->_y += rhs.__vbcPoint2d->_y; 
_z += rhs._z; 
  • There are two primary weaknesses with this implementation model:

    • An object of the class carries an additional pointer for each virtual base class. Ideally, we want a constant overhead for the class object that is independent of the number of virtual base classes within its inheritance hierarchy. Think of how you might solve this.

    • As the virtual inheritance chain lengthens, the level of indirection increases to that depth. This means that three levels of virtual derivation requires indirection through three virtual base class pointers. Ideally, we want a constant access time regardless of the depth of the virtual derivation.

Data Layout: Virtual Inheritance with Pointer Strategy
image

There are two general solutions to the first problem.Microsoft’s compiler introduced the virtual base class table. Each class object with one or more virtual base classes has a pointer to the virtual base class table inserted within it. The actual virtual base class pointers, of course, are placed within the table. Although this solution has been around for many years, I am not aware of any other compiler implementation that employs it. (It may be that Microsoft’s patenting of their virtual function implementation effectively prohibits its use.)

The second solution, and the one preferred by Bjarne (at least while I was working on the Foundation project with him), is to place not the address but the offset of the virtual base class within the virtual function table. (Figure 3.5(b) on page 100 shows the base class offset implementation model.) I implemented this in the Foundation research project, interweaving the virtual base class and virtual function entries. In the recent Sun compiler, the virtual function table is indexed by both positive and negative indices. The positive indices, as previously, index into the set of virtual functions; the negative indices retrieve the virtual base class offsets. Under this strategy, the Point3d operator is translated into the following general form (leaving off casts for readability and not showing the more efficient precalculation of the addresses):

  • Data Layout: Virtual Inheritance with Virtual Table Offset Strategy
    image
// Pseudo C++ Code 
(this + __vptr__Point3d[-1])->_x += 
   (&rhs + rhs.__vptr__Point3d[-1])->_x; 
(this + __vptr__Point3d[-1])->_y += 
   (&rhs + rhs.__vptr__Point3d[-1])->_y; 
_z += rhs._z;

3.6 Pointer to Data Members

&Point3D::x //值代表x在Point3D里的偏移量

Because inherited data members are stored directly within the class object, the introduction of inheritance does not affect the performance of the code at all. The major impact of introducing virtual inheritance is to impede the effectiveness of the optimizer.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值