文章目录
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.)
Multiple Inheritance
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
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
// 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.