5.12.5.2.2.2.1.3.9. 完成派生类的 RECORD_TYPE – 布局
回到 finish_struct_1 中,下面的 keyed_classes 是一个保存那些在这个编译单元中将可能发布 vtable 的类的 tree_list 。这个链表中的候选者是不含有非内联、非纯虚方法的类。换而言之,是仅含有纯虚方法或(及)内联虚方法的类。对于这样的类,在每个该类定义出现的编译单元中(如,通过 #include ),应该发布其 vtable ;而对于含有其它虚方法的类,每个包含了其虚方法定义的编译单元要发布其 vtable 。
finish_struct_1 (continue)
5033 /* Find the key method. */
5034 if (TYPE_CONTAINS_VPTR_P (t))
5035 {
5036 CLASSTYPE_KEY_METHOD (t) = key_method (t);
5037
5038 /* If a polymorphic class has no key method, we may emit the vtable
5039 i n every translation unit where the class definition appears. */
5040 if (CLASSTYPE_KEY_METHOD (t) == NULL_TREE)
5041 keyed_classes = tree_cons (NULL_TREE, t, keyed_classes );
5042 }
5043
5044 /* Layout the class itself. */
5045 layout_class_type (t, &virtuals);
5046 if (CLASSTYPE_AS_BASE (t) != t)
5047 /* We use the base type for trivial assignments, and hence it
5048 needs a mode. */
5049 compute_record_mode (CLASSTYPE_AS_BASE (t));
CLASSTYPE_KEY_METHOD 保存了类中第一个非内联及非纯虚方法 ,这个域在 vtable 的产生过程中要用到。
4973 static tree
4974 key_method (tree type) in class.c
4975 {
4976 tree method;
4977
4978 if (TYPE_FOR_JAVA (ty pe)
4979 || processing_template_decl
4980 || CLASSTYPE_TEMPLATE_INSTANTIATION (type)
4981 || CLASSTYPE_INTERFACE_KNOWN (type))
4982 return NULL_TREE;
4983
4984 for (method = TYPE_METHODS (type); method != NULL_TREE;
4985 method = TREE_CHAIN (method))
4986 if (DECL_VINDEX (method) != NULL_TREE
4987 && ! DECL_DECLARED_INLINE_P (method)
4988 && ! DECL_PURE_VIRTUAL_P (method))
4989 return method;
4990
4991 return NULL_TREE;
4992 }
记住在类模板中是不允许出现虚函数的。
如果我们已经确定了是否要在该编译单元中产生 vtable , VTT , typeinfo 及其他类似的类维护数据, CLASSTYPE_INTERFACE_KNOWN 是 true ,这样的类即便满足上述条件,也不应该被保存在 keyed_classes 中。
接着我们可以为类布局来决定类型的真正的大小,每个数据成员的偏移量,大小,填充字节,甚至由编译器产生的维护数据。毫无疑问,这是一个相当复杂的过程。
在深入到代码之前,我们首先看一下 GCC 为下面的例子所做的布局:
例 1 :
class A { virtual A* f (); };
class B1 : virtual public A { virtual B1* f (); };
class B2 : virtual public A { virtual B2* f (); };
class C: public B1, public B2 { virtual C* f (); };
int main() { return 0; }
用命令 g++ -fdump-class-hierarchy –o test test.cc , gcc 将把关于类的信息转储到一个转储文件(如: test.01.class )中,我们可以看到如下的输出(暂时忽略布局以外的信息):
…
Class A
size=4 align=4
base size=4 base align=4
A (0xb7f2d680) 0 nearly-empty
vptr=((& A::_ZTV1A) + 8u)
…
Class B1
size=4 align=4
base size=4 base align=4
B1 (0xb7f2d700) 0 nearly-empty
vptridx=0u vptr=((& B1::_ZTV2B1) + 16u)
A (0xb7f2d740) 0 nearly-empty virtual
primary-for B1 (0xb7f2d700)
vptridx=4u vbaseoffset=-0x000000010
…
Class B2
size=4 align=4
base size=4 base align=4
B2 (0xb7f2d840) 0 nearly-empty
vptridx=0u vptr=((& B2::_ZTV2B2) + 16u)
A (0xb7f2d880) 0 nearly-empty virtual
primary-for B2 (0xb7f2d840)
vptridx=4u vbaseoffset=-0x000000010
…
Class C
size=8 align=4
base size=8 base align=4
C (0xb7f2d940) 0
vptridx=0u vptr=((& C::_ZTV1C) + 16u)
B1 (0xb7f2d980) 0 nearly-empty
primary-for C (0xb7f2d940)
subvttidx=4u
A (0xb7f2d9c0) 0 nearly-empty virtual
primary-for B1 (0xb7f2d980)
vptridx=20u vbaseoffset=-0x000000010
B2 (0xb7f2da00) 4 nearly-empty
lost-primary
subvttidx=12u vptridx=24u vptr=((& C::_ZTV1C) + 40u)
A (0xb7f2d9c0) alternative-path
在输出“ A (0xb7f2d680) 0 nearly-empty ”中,“ A ”是 binfo 所来自的类,“ (0xb7f2d680) ”是 binfo 的地址,“ 0 ”是 binfo 在类布局中的偏移量,“ nearly-empty ”表明该类是一个几乎空的类。
而输出“ size=8 align=4 ”是该类的大小及对齐量,而“ base size=8 base align=4 ”是该类的 CLASSTYPE_AS_BASE (去除虚拟基类)的大小及对齐量。
例 2 (在类 A 中增加域“ int a ”):
class A { int a; virtual A* f (); };
class B1 : virtual public A { virtual B1* f (); };
class B2 : virtual public A { virtual B2* f (); };
class C: public B1, public B2 { virtual C* f (); };
int main() { return 0; }
其输出则是:
…
Class A
size=8 align=4
base size=8 base align=4
A (0xb7fae680) 0
vptr=((& A::_ZTV1A) + 8u)
…
Class B1
size=12 align=4
base size=4 base align=4
B1 (0xb7fae700) 0 nearly-empty
vptridx=0u vptr=((& B1::_ZTV2B1) + 12u)
A (0xb7fae740) 4 virtual
vptridx=4u vbaseoffset=-0x00000000c vptr=((& B1::_ZTV2B1) + 28u)
…
Class B2
size=12 align=4
base size=4 base align=4
B2 (0xb7fae800) 0 nearly-empty
vptridx=0u vptr=((& B2::_ZTV2B2) + 12u)
A (0xb7fae840) 4 virtual
vptridx=4u vbaseoffset=-0x00000000c vptr=((& B2::_ZTV2B2) + 28u)
…
Class C
size=16 align=4
base size=8 base align=4
C (0xb7fae8c0) 0
vptridx=0u vptr=((& C::_ZTV1C) + 12u)
B1 (0xb7fae900) 0 nearly-empty
primary-for C (0xb7fae8c0)
subvttidx=4u
A (0xb7fae940) 8 virtual
vptridx=20u vbaseoffset=-0x00000000c vptr=((& C::_ZTV1C) + 44u)
B2 (0xb7fae980) 4 nearly-empty
subvttidx=12u vptridx=24u vptr=((& C::_ZTV1C) + 28u)
A (0xb7fae940) alternative-path
通过引入域“ int a “,类 A 不再是一个几乎空的类,但 B1 及 B2 还是。这一事实深刻地影响着布局。在下面的代码中我们可以找到其原因。
4637 static void
4638 layout_class_type (tree t, tree *virtuals_p) in class.c
4639 {
4640 tree non_static_data_members;
4641 tree field;
4642 tree vptr;
4643 record_layout_info rli;
4644 /* Maps offsets (represented as INTEGER_CSTs) to a TREE_LIST of
4645 types that appear at that offset. */
4646 splay_tree empty_base_offsets;
4647 /* True if the last field layed out was a bit-field. */
4648 bool last_field_was_bitfield = false;
4649 /* The location at which the next field should be inserted. */
4650 tree *next_field;
4651 /* T, as a base class. */
4652 tree base_t;
4653
4654 /* Keep track of the first non-static data member. */
4655 non_static_data_members = TYPE_FIELDS (t);
4656
4657 /* Start laying out the record. */
4658 rli = start_record_layout (t);
4659
4660 /* If possible, we reuse the virtual function table pointer from one
4661 of our base classes. */
4662 determine_primary_base (t);
在多继承的情况下,在派生类中基类将被依次编排。第一个基类有偏移量 0 ,无疑可以从派生类的头部直接访问其成员,而其它随后的基类,要访问其成员,其成员的偏移量要加上基类本身到派生类头部的偏移量,等于包含了一个额外的间接寻址。加上深度继承的情形,需要仔细选择第一个编排的基类。注意到这里每个类定义都调用了 determine_primary_base 。如果我们为一个复杂类来调用它,其基类应该已经被这个函数处理过了。
1267 static void
1268 determine_primary_base (tree t) in class.c
1269 {
1270 int i, n_baseclasses = CLASSTYPE_N_BASECLASSES (t);
1271 tree vbases;
1272 tree type_binfo;
1273
1274 /* If there are no baseclasses, there is certainly no primary base. */
1275 if (n_baseclasses == 0)
1276 return ;
1277
1278 type_binfo = TYPE_BINFO (t);
1279
1280 for (i = 0; i < n_baseclasses; i++)
1281 {
1282 tree base_binfo = BINFO_BASETYPE (type_binfo, i);
1283 tree basetype = BINFO_TYPE (base_binfo);
1284
1285 if (TYPE_CONTAINS_VPTR_P (basetype))
1286 {
1287 /* We prefer a non-virtual base, although a virtual one will
1288 do. */
1289 if (TREE_VIA_VIRTUAL (base_binfo))
1290 continue ;
1291
1292 if (!CLASSTYPE_HAS_PRIMARY_BASE_P (t))
1293 {
1294 set_primary_base (t, base_binfo);
1295 CLASSTYPE_VFIELDS (t) = copy_list (CLASSTYPE_VFIELDS (basetype));
1296 }
1297 else
1298 {
1299 tree vfields;
1300
1301 /* Only add unique vfields, and flatten them out as we go. */
1302 for (vfields = CLASSTYPE_VFIELDS (basetype);
1303 vfields;
1304 vfields = TREE_CHAIN (vfields))
1305 if (VF_BINFO_VALUE (vfields) == NULL_TREE
1306 || ! TREE_VIA_VIRTUAL (VF_BINFO_VALUE (vfields)))
1307 CLASSTYPE_VFIELDS (t)
1308 = tree_cons (base_binfo,
1309 VF_BASETYPE_VALUE (vfields),
1310 CLASSTYPE_VFIELDS (t));
1311 }
1312 }
1313 }
含有 vtable 的非虚拟基类是最好的选择。碰到的第一个这样的基类被下面的 set_primary_base 与派生类绑定起来。注意 TYPE_VFIELD 是由编译器产生的指向 vtable 的域。
1253 static void
1254 set_primary_base (tree t, tree binfo) in class.c
1255 {
1256 tree basetype;
1257
1258 CLASSTYPE_PRIMARY_BINFO (t) = binfo;
1259 basetype = BINFO_TYPE (binfo);
1260 TYPE_BINFO_VTABLE (t) = TYPE_BINFO_VTABLE (basetype);
1261 TYPE_BINFO_VIRTUALS (t) = TYPE_BINFO_VIRTUALS (basetype);
1262 TYPE_VFIELD (t) = TYPE_VFIELD (basetype);
1263 }
上面的域 CLASSTYPE_VFIELDS 是这个类型所持有的 vtable 链表(主要及次要 vtable )。其节点中的 TREE_VALUE 是引入这个 vtable 的类型。对于引入该 vtable ,或从其主要基类继承该 vtable 的类,节点的 TREE_PURPOSE 则是 NULL 。对于其它 vtable (来自次要基类),其节点的 TREE_PURPOSE 是该 vtable 所来自的基类的 BINFO 。
注意派生类的 CLASSTYPE_VFIELDS 开始是空的;而且虚拟基类,除非它几乎是空的(这时它不可能有 vtable ),是不能作为派生类的主要基类(就算它是唯一有 vtable 的基类)。因此上面对 vtable 的拷贝能确保只对非虚拟基类进行。并且注意如果是主要基类,派生类直接拷贝其 CLASSTYPE_VFIELDS (用于这个主要基类 vtable 的节点的 PURPOSE 域为 NULL )。这是因为主要基类就在派生类的头部,如果其有 vtable ,这个 vtable 的地址也就是派生类 vtable 所在地址,派生类将直接把这个 vtable 据为己有。
determine_primary_base (continue)
1315 if (!TYPE_VFIELD (t))
1316 CLASSTYPE_PRIMARY_BINFO (t) = NULL_TREE;
1317
1318 /* Find the indirect primary bases - those virtual bases which are primary
1319 bases of something else in this hierarchy. */
1320 for (vbases = CLASSTYPE_VBASECLASSES (t);
1321 vbases;
1322 vbases = TREE_CHAIN (vbases))
1323 {
1324 tree vbase_binfo = TREE_VALUE (vbases);
1325
1326 /* See if this virtual base is an indirect primary base. To be so,
1327 it must be a primary base within the hierarchy of one of our
1328 direct bases. */
1329 for (i = 0; i < n_baseclasses; ++i)
1330 {
1331 tree basetype = TYPE_BINFO_BASETYPE (t, i);
1332 tree v;
1333
1334 for (v = CLASSTYPE_VBASECLASSES (basetype);
1335 v;
1336 v = TREE_CHAIN (v))
1337 {
1338 tree base_vbase = TREE_VALUE (v);
1339
1340 if (BINFO_PRIMARY_P (base_vbase)
1341 && same_type_p (BINFO_TYPE (base_vbase),
1342 BINFO_TYPE (vbase_binfo)))
1343 {
1344 BINFO_INDIRECT_PRIMARY_P (vbase_binfo) = 1;
1345 break ;
1346 }
1347 }
1348
1349 /* If we've discovered that this virtual base is an indirect
1350 primary base, then we can move on to the next virtual
1351 base. */
1352 if (BINFO_INDIRECT_PRIMARY_P (vbase_binfo))
1353 break ;
1354 }
1355 }
1356
1357 /* A "nearly-empty" virtual base class can be the primary base
1358 class, if no non-virtual polymorphic base can be found. */
1359 if (!CLASSTYPE_HAS_PRIMARY_BASE_P (t))
1360 {
1361 /* If not NULL, this is the best primary base candidate we have
1362 found so far. */
1363 tree candidate = NULL_TREE;
1364 tree base_binfo;
1365
1366 /* Loop over the baseclasses. */
1367 for (base_binfo = TYPE_BINFO (t);
1368 base_binfo;
1369 base_binfo = TREE_CHAIN (base_binfo))
1370 {
1371 tree basetype = BINFO_TYPE (base_binfo);
1372
1373 if (TREE_VIA_VIRTUAL (base_binfo)
1374 && CLASSTYPE_NEARLY_EMPTY_P (basetype))
1375 {
1376 /* If this is not an indirect primary base, then it's
1377 definitely our primary base. */
1378 if (!BINFO_INDIRECT_PRIMARY_P (base_binfo))
1379 {
1380 candidate = base_binfo;
1381 break ;
1382 }
1383
1384 /* If this is an indirect primary base, it still could be
1385 our primary base -- unless we later find there's another
1386 nearly-empty virtual base that isn't an indirect
1387 primary base. */
1388 if (!candidate)
1389 candidate = base_binfo;
1390 }
1391 }
1392
1393 /* If we've got a primary base, use it. */
1394 if (candidate)
1395 {
1396 set_primary_base (t, candidate);
1397 CLASSTYPE_VFIELDS (t)
1398 = copy_list (CLASSTYPE_VFIELDS (BINFO_TYPE (candidate)));
1399 }
1400 }
1401
1402 /* Mark the primary base classes at this point. */
1403 mark_primary_bases (t);
1404 }
如果没有找到含有 vtable 的非虚拟基类,派生类的 TYPE_VFIELD 保持为 NULL (在 set_primary base 中,它会被设置为主要基类的 TYPE_VFIELD ),这时不管以前把 CLASSTYPE_PRIMARY_BINFO 设置成什么,把它置为 NULL 。接着看看能不能在虚拟基类中找到可以作为主要基类的。能作为主要基类的虚拟基类,除了需要是几乎空的之外,没有被用作其基类的主要基类是优先考虑的,否则就将就使用在派生类声明中最后出现的虚拟基类(这样也减少了冲突的机会)。
严格来说,在一个派生类中只有一个主要基类。例如:
class A {…}; class B: public A {…}; class C: public B {…};
此中类 B 的主要基类是 A 。而在类 C 中,主要基类是 B ,不再是 A 。但是 C 中关于 B 的 binfo 是从类 B 中拷贝过来的,拷贝时没有拷入关于主要基类的信息。不过虚拟基类则不然,下面的函数还会对它进行“拨乱反正”。
1218 static void
1219 mark_primary_bases (tree type) in class.c
1220 {
1221 tree binfo;
1222
1223 /* Walk the bases in inheritance graph order. */
1224 for (binfo = TYPE_BINFO (type); binfo; binfo = TREE_CHAIN (binfo))
1225 {
1226 tree base_binfo = get_primary_binfo (binfo);
1227
1228 if (!base_binfo)
1229 /* Not a dynamic base. */ ;
1230 else if (BINFO_PRIMARY_P (base_binfo))
1231 BINFO_LOST_PRIMARY_P (binfo) = 1;
1232 else
1233 {
1234 BINFO_PRIMARY_BASE_OF (base_binfo) = binfo;
1235 /* A virtual binfo might have been copied from within
1236 another hierarchy. As we're about to use it as a primary
1237 base, make sure the offsets match. */
1238 if (TREE_VIA_VIRTUAL (base_binfo))
1239 {
1240 tree delta = size_diffop (convert (ssizetype,
1241 BINFO_OFFSET (binfo)),
1242 convert (ssizetype,
1243 BINFO_OFFSET (base_binfo)));
1244
1245 propagate_binfo_offsets (base_binfo, delta);
1246 }
1247 }
1248 }
1249 }
在 1226 行的 get_primary_binfo 返回参数 binfo 所对应类型的主要基类的 binfo 的拷贝,如果设置了主要基类的话;否则返回 NULL 。
6470 tree
6471 get_primary_binfo (tree binfo) in class.c
6472 {
6473 tree primary_base;
6474 tree result;
6475
6476 primary_base = CLASSTYPE_PRIMARY_BINFO (BINFO_TYPE (binfo));
6477 if (!primary_base)
6478 return NULL_TREE;
6479
6480 result = copied_binfo (primary_base, binfo);
6481 return result;
6482 }
copied_binfo 根据给定的 primary_base ,从 binfo 中找出对应的部分。考虑以下例子:
class A { virtual … };
class B: public A { virtual … };
class C: public A { virtual … };
class D: public B, C { … };
首先,在 mark_primary_bases 的 1224 行, TYPE_BINFO(D) 得到一个链表,其中依次是 D à B à A à C à A 。显然, CLASSTYPE_PRIMARY_BINFO(D) 返回 B 的 binfo 拷贝(它由 set_primary_base 设置);然后在下面的 2578 行,递归进入 copied_binfo 并得到 D 的 binfo ,接着在 2581 行的 FOR 循环中,在 D 的基类 B 、 C 中查找,找出目标 B 。因为该 B 的 binfo 拷贝来自类 B , 1230 行的 BINFO_PRIMARY_P (base_binfo) 返回 NULL ,这表示 B 不是主要基类(但现在是!)。因此在 1234 行,我们把它设为 D 的主要基类(注意与 CLASSTYPE_PRIMARY_BINFO 的区别)。
那么对于 B 的 binfo , A 是其主要基类。在 D 中查找 A 的 binfo 拷贝与查找 B 的 binfo 拷贝类似,除了在 D 中 A 同时出现在 B 及 C 中。不过查找的过程能保证找到的是 B à A 。因为 A 是类 B 的主要基类,但在 A 拷贝的 binfo 中,并没有设置 BINFO_PRIMARY_P ,它满足 1232 行条件,被设置为 B 的拷贝 binfo 的主要基类。
同样的我们可以得到 D 中 C 部分的 A 的 binfo 拷贝,并且它也没有设置 BINFO_PRIMARY_P 。
2556 tree
2557 copied_binfo (tree binfo, tree here) in search.c
2558 {
2559 tree result = NULL_TREE;
2560
2561 if (TREE_VIA_VIRTUAL (binfo))
2562 {
2563 tree t;
2564
2565 for (t = here; BINFO_INHERITANCE_CHAIN (t);
2566 t = BINFO_INHERITANCE_CHAIN (t))
2567 continue ;
2568
2569 result = purpose_member (BINFO_TYPE (binfo),
2570 CLASSTYPE_VBASECLASSES (BINFO_TYPE (t)));
2571 result = TREE_VALUE (result);
2572 }
2573 else if (BINFO_INHERITANCE_CHAIN (binfo))
2574 {
2575 tree base_binfos;
2576 int ix, n;
2577
2578 base_binfos = copied_binfo (BINFO_INHERITANCE_CHAIN (binfo), here);
2579 base_binfos = BINFO_BASETYPES (base_binfos);
2580 n = TREE_VEC_LENGTH (base_binfos);
2581 for (ix = 0; ix != n; ix++)
2582 {
2583 tree base = TREE_VEC_ELT (base_binfos, ix);
2584
2585 if (BINFO_TYPE (base) == BINFO_TYPE (binfo))
2586 {
2587 result = base;
2588 break ;
2589 }
2590 }
2591 }
2592 else
2593 {
2594 my_friendly_assert (BINFO_TYPE (here) == BINFO_TYPE (binfo), 20030202);
2595 result = here;
2596 }
2597
2598 my_friendly_assert (result, 20030202);
2599 return result;
2600 }
进一步考虑如下的例子:
class A { virtual …};
class B: virtual public A { … };
class C: virtual public A { … };
class D: public B, C { … };
在 TYPE_BINFO(D) 链表中,依次是 D à B à A à C ,因为 A 作为虚拟基类在 D 中只能维持一个实例。这个过程是几乎相同的,除了查找虚拟基类的 binfo 拷贝时,是从继承树顶开始向下,而不是从基类开始往上爬。另外,由于虚拟基类 A 不使用拷贝 binfo ,因此在 mark_primary_bases 中,它满足 1230 行条件。
在 copy_base_binfos 中,连同 binfo 拷贝的还有偏移量信息。不过这个偏移量是关于被拷贝的 binfo 。而作为主要基类,虚拟基类(也包括非虚拟基类)必须被放在布局的最前面(事实上紧跟着 vtable )。这个调整由 propagate_binfo_offsets 来完成(对于非虚拟的基类 binfo ,它的偏移量一直是 0 ,并且在当前类中编排该基类时,其非虚拟基类也维持着不变的相对偏移量)。
4355 static void
4356 propagate_binfo_offsets (tree binfo, tree offset) in class.c
4357 {
4358 int i;
4359 tree primary_binfo;
4360
4361 /* Update BINFO's offset. */
4362 BINFO_OFFSET (binfo)
4363 = convert (sizetype,
4364 size_binop (PLUS_EXPR,
4365 convert (ssizetype, BINFO_OFFSET (binfo)),
4366 offset));
4367
4368 /* Find the primary base class. */
4369 primary_binfo = get_primary_binfo (binfo);
4370
4371 /* Scan all of the bases, pushing the BINFO_OFFSET adjust
4372 downwards. */
4373 for (i = -1; i < BINFO_N_BASETYPES (binfo); ++i)
4374 {
4375 tree base_binfo;
4376
4377 /* On the first time through the loop, do the primary base.
4378 Because the primary base need not be an immediate base, we
4379 must handle the primary base specially. */
4380 if (i == -1)
4381 {
4382 if (!primary_binfo)
4383 continue ;
4384
4385 base_binfo = primary_binfo;
4386 }
4387 else
4388 {
4389 base_binfo = BINFO_BASETYPE (binfo, i);
4390 /* Don't do the primary base twice. */
4391 if (base_binfo == primary_binfo)
4392 continue ;
4393 }
4394
4395 /* Skip virtual bases that aren't our canonical primary base. */
4396 if (TREE_VIA_VIRTUAL (base_binfo)
4397 && BINFO_PRIMARY_BASE_OF (base_binfo) != binfo)
4398 continue ;
4399
4400 propagate_binfo_offsets (base_binfo, offset);
4401 }
4402 }
函数 propagate_binfo_offsets 把偏移量的调整应用到整个基类。记得 BINFO_OFFSET 表示该基类在其派生类中的偏移量。前面看到主要基类不一定是直接基类,因此 4373 行的 FOR 循环需要多一次计数来包括可能不是直接基类的主要基类。