GCC-3.4.6源代码学习笔记(162)

5.13.4.7.1.        为函数调用产生代码
5.13.4.7.1.1.  构建获取函数地址的表达式

当我们写出类似: f (a, b); 的代码来调用 f ,编译器需要努力使得这个调用以期望的方式可行。下面的函数泄露了编译器的所作所为。

 

2420   tree

2421   build_function_call (tree function, tree params)                                          in typeck.c

2422   {

2423     tree fntype, fndecl;

2424     tree coerced_params;

2425     tree result;

2426     tree name = NULL_TREE, assembler_name = NULL_TREE;

2427     int is_method;

2428     tree original = function;

2429  

2430     /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.

2431       Strip such NOP_EXPRs, since FUNCTION is used in non-lvalue context.  */

2432     if (TREE_CODE (function) == NOP_EXPR

2433         && TREE_TYPE (function) == TREE_TYPE (TREE_OPERAND (function, 0)))

2434       function = TREE_OPERAND (function, 0);

2435  

2436     if (TREE_CODE (function) == FUNCTION_DECL)

2437     {

2438       name = DECL_NAME (function);

2439       assembler_name = DECL_ASSEMBLER_NAME (function);

2440  

2441       mark_used (function);

2442       fndecl = function;

2443  

2444       /* Convert anything with function type to a pointer-to-function.  */

2445        if (pedantic && DECL_MAIN_P (function))

2446         pedwarn ("ISO C++ forbids calling `::main' from within program");

2447  

2448       /* Differs from default_conversion by not setting TREE_ADDRESSABLE

2449         (because calling an inline function does not mean the function

2450         needs to be separately compiled).  */

2451        

2452       if (DECL_INLINE (function))

2453         function = inline_conversion (function);

2454       else

2455         function = build_addr_func (function);

2456     }

2457     else

2458     {

2459       fndecl = NULL_TREE;

2460  

2461       function = build_addr_func (function);

2462     }

 

在调用点内联函数被展开。因此它不能在该编译单元之外分开编译,也不能把它标记为 TREE_ADDRESSABLE 。在这里 ADDR_EXPR 作为这个调用语句的结果被产生出来。

 

1464   tree

1465   inline_conversion (tree exp)                                                                             in typeck.c

1466   {

1467     if (TREE_CODE (exp) == FUNCTION_DECL)

1468       exp = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (exp)), exp);

1469  

1470     return exp;

1471   }

 

当通过指针来访问内联函数(以形式 x.*p ,其中 x 是可取址的,因而 build_address (它要求可取址的对象作为参数)可以工作),访问性检查是要求的;而对于调用普通函数,需要 decay_conversion 来进行函数到指针的转换。

 

178    tree

179    build_addr_func (tree function)                                                                              in tree.c

180    {

181      tree type = TREE_TYPE (function);

182   

183      /* We have to do these by hand to avoid real pointer to member

184        functions.  */

185      if (TREE_CODE (type) == METHOD_TYPE)

186      {

187        if (TREE_CODE (function) == OFFSET_REF)

188        {

189          tree object = build_address (TREE_OPERAND (function, 0));

190          return get_member_function_from_ptrfunc (&object,

191                                                            TREE_OPERAND (function, 1));

192        }

193        function = build_address (function);

194      }

195      else

196        function = decay_conversion (function);

197   

198      return function;

199  }

 

上面的 OFFSET_REF 被用在两种情形中:

1. 一个具有形式‘ A::m ’的表达式,其中‘ A ’是一个类,而‘ m ’是一个非静态成员。在这个情况下,操作数 0 将是一个 TYPE (对应‘ A ’),而操作数 1 将是一个 FIELD_DECL BASELINK ,或 TEMPLATE_ID_EXPR (对应‘ m ’)。

如果其地址被获取了,这个表达式就是一个指向成员的指针,但如果其地址没有被获取,则仅表示该对象的一个成员。

这个形式仅被用在解析阶段;一旦语法分析完成,该形式就要被消除。

2. 一个具有形式‘ x.*p ’的表达式。在这个情况下,操作数 0 将是一个对应‘ x ’的表达式,而操作数 1 将是一个具有指向成员的指针类型的表达式。

显然,类方法的指针遵守第二个规则。对于非静态方法的调用,记得还有一个隐含的“ const this* ”,上面的 189 行, object 就是这个 this 指针。另外在 C++ 中,有一个奇特的类型 方法类型【 14 】,例如:“ typedef void (C::*Cf) () = &C::f; Cf C (假定 C 是一个类)的一个方法类型,它没有参数,并返回 void 2324 行的谓词 TYPE_PTRMEMFUNC_P 对这样的类型返回 true 。在 2327 行的 TYPE_PTRMEMFUNC_FN_TYPE 获取关联的方法类型。

 

2318   tree

2319   get_member_function_from_ptrfunc (tree *instance_ptrptr, tree function)    in typeck.c

2320   {

2321     if (TREE_CODE (function) == OFFSET_REF)

2322       function = TREE_OPERAND (function, 1);

2323  

2324     if (TYPE_PTRMEMFUNC_P (TREE_TYPE (function)))

2325     {

2326       tree idx, delta, e1, e2, e3, vtbl, basetype;

2327       tree fntype = TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (function));

2328  

2329       tree instance_ptr = *instance_ptrptr;

2330       tree instance_save_expr = 0;

2331       if (instance_ptr == error_mark_node)

2332       {

2333         if (TREE_CODE (function) == PTRMEM_CST)

2334            {

2335              /* Extracting the function address from a pmf is only

2336              allowed with -Wno-pmf-conversions. It only works for

2337              pmf constants.  */

2338              e1 = build_addr_func (PTRMEM_CST_MEMBER (function));

2339              e1 = convert (fntype, e1);

2340              return e1;

2341         }

2342         else

2343         {

2344           error ("object missing in use of `%E'", function);

2345           return error_mark_node;

2346         }

2347       }

2348  

2349       if (TREE_SIDE_EFFECTS (instance_ptr))

2350         instance_ptr = instance_save_expr = save_expr (instance_ptr);

2351  

2352       if (TREE_SIDE_EFFECTS (function))

2353         function = save_expr (function);

2354  

2355       /* Start by extracting all the information from the PMF itself.  */

2356       e3 = PFN_FROM_PTRMEMFUNC (function);

 

如果 instance_ptr error_mark_node ,在这里 function 应该是一个 PTRMEM_CST (成员指针常量的节点),其中的 PTRMEM_CST_CLASS C RECORD_TYPE ,而 PTRMEM_CST_MEMBER 是对应 f *_DECL (不过我想不出一个会产生这样节点的正常情形,因为上面的 build_address 189 行),对于 instance_ptr ,总是成功的,除非传入的节点是 error_mark_node ,这意味着在这个过程中某些东西出错了。即便对于把方法指针转换到函数指针的情形,似乎也不应该到这里来,其细节可以在【 6 】中看到)。

C++ 中,成员方法的指针( PMF )使用一个差不多可以处理所有可能调用机制的宽指针来实现; PMF 需要保存关于如何调整‘ this ’指针的信息,并且如果被指向的函数是虚函数,在何处查找 vtable ,及在该 vtable 的何处查找这个成员方法。如果你在一个内部循环中使用 PMF ,你应该三思。如果别无选择,你可以从该指针提取将为给定对象 /PMF 对所调用的函数,并在这个内部循环中直接调用这个函数,以节约一些时间。

注意到你仍将为通过一个函数指针进行的函数调用付出代价;在绝大多数架构上,这样的一个调用会挫败 CPU 的分支预测特性。这对于普通的虚函数调用也是成立的。

这个扩展的语法是

extern A a;

extern int (A::*fp)();

typedef int (*fptr)(A *);

fptr p = (fptr)(a.*fp);

对于 PMF 常量( 具有形式 &Klasse::Member 的表达式 ), 不需要对象来获取该函数的地址。它们可以被直接转换到函数指针:

fptr p1 = (fptr)(&A::foo);

你必须指定‘ -Wno-pmf-conversions ’来使用这个扩展。

而如果 instance_ptr 不是 error_mark_node ,在 2356 行的 function 是具有成员指针类型的表达式,而 PFN_FROM_PTRMEMFUNC 具有如下定义。

 

2563   #define PFN_FROM_PTRMEMFUNC (NODE) pfn_from_ptrmemfunc ((NODE))

 

5637   tree

5638   pfn_from_ptrmemfunc (tree t)                                                                  in typeck.c

5639   {

5640     if (TREE_CODE (t) == PTRMEM_CST)

5641     {

5642       tree delta;

5643       tree pfn;

5644        

5645       expand_ptrmemfunc_cst (t, &delta, &pfn);

5646       if (pfn)

5647         return pfn;

5648     }

5649  

5650     return build_ptrmemfunc_access_expr (t, pfn_identifier );

5651   }

 

上面的函数获取了由这个成员指针类型的表达式或 PTRMEM_CST 结构所引用的方法,并且它需要调整 this 指针,或适当地操控 vtable

首先,如果上面的实参 function 具有形如:“ &C::f ”的形式,它是应该 PTRMEM_CST 节点。不过在构建函数调用的上下文中, function 不会是 PTRMEM_CST ;而 function PTRMEM_CST 的一个场景是形如:“ void (B::*pfn)() = &B::f; ”的语句,然后 expand_ptrmemfunc_cst 将被 build_ptrmemfunc 调用来从“ &B::f ”的 PTRMEM_CST 中,得到用于方法指针类型“ void (B::*)() ”的结构(见下面)的 __pfn __delta

不管怎么说,这值得我们花一些时间看一下怎样为方法指针确定 __pfn __delta 域。下面在 5587 行的 TYPE_PTRMEMFUNC_OBJECT_TYPE 为形如“ int (A::*)(double) ”的类型返回‘ A ’。

 

5574   void

5575   expand_ptrmemfunc_cst (tree cst, tree *delta, tree *pfn)                                   in typeck.c

5576   {

5577     tree type = TREE_TYPE (cst);

5578     tree fn = PTRMEM_CST_MEMBER (cst);

5579     tree ptr_class, fn_class;

5580  

5581     my_friendly_assert (TREE_CODE (fn) == FUNCTION_DECL, 0);

5582  

5583     /* The class that the function belongs to.  */

5584     fn_class = DECL_CONTEXT (fn);

5585  

5586     /* The class that we're creating a pointer to member of.  */

5587     ptr_class = TYPE_PTRMEMFUNC_OBJECT_TYPE (type);

5588  

5589     /* First, calculate the adjustment to the function's class.  */

5590     *delta = get_delta_difference (fn_class, ptr_class, /*force=*/ 0);

 

考虑下面的例子:

class A { public : void f (); };

class B: public A {};

void (B::*pfn) () = &B::f;

对于这个例子, fn_class 将是 A (来自“ &B::f ”部分), ptr_class 将是 B (来自“ vod (B::*pfn) () ”部分),而 fn 将是 f 。因此,首先需要知道派生类与定义该函数的基类间的调整量。在下面的函数中,参数 force 如果是 false ,表示不允许反向的转换(从 to from )。

 

5392   static tree

5393   get_delta_difference (tree from, tree to, int force)                                       in typeck.c

5394   {

5395     tree binfo;

5396     tree virt_binfo;

5397     base_kind kind;

5398    

5399     binfo = lookup_base (to, from, ba_check, &kind);

5400     if (kind == bk_inaccessible || kind == bk_ambig)

5401     {

5402       error ("   in pointer to member function conversion");

5403       goto error;

5404     }

5405     if (!binfo)

5406     {

5407       if (!force)

5408       {

5409         error_not_base_type (from, to);

5410          error ("   in pointer to member conversion");

5411         goto error;

5412       }

5413       binfo = lookup_base (from, to, ba_check, &kind);

5414       if (!binfo)

5415         goto error;

5416       virt_binfo = binfo_from_vbase (binfo);

5417       if (virt_binfo)

5418       {

5419         /* This is a reinterpret cast, we choose to do nothing.  */

5420         warning ("pointer to member cast via virtual base `%T'",

5421         BINFO_TYPE (virt_binfo));

5422         goto error;

5423       }

5424       return fold (convert_to_integer (ptrdiff_type_node ,

5425                                 size_diffop (size_zero_node,

5426                                           BINFO_OFFSET (binfo))));

5427     }

5428  

5429     virt_binfo = binfo_from_vbase (binfo);

5430     if (!virt_binfo)

5431       return fold (convert_to_integer (ptrdiff_type_node , BINFO_OFFSET (binfo)));

5432  

5433     /* This is a reinterpret cast, we choose to do nothing.  */

5434     if (force)

5435       warning ("pointer to member cast via virtual base `%T'",

5436                BINFO_TYPE (virt_binfo));

5437     else

5438       error ("pointer to member conversion via virtual base `%T'",

5439           BINFO_TYPE (virt_binfo));

5440  

5441   error:

5442     return fold (convert_to_integer(ptrdiff_type_node , integer_zero_node));

5443   }

 

注意到参数 from to 必须具有继承关系。另外,如果转换涉及虚拟基类,这是仅可以被 reinterpret_cast 处理的语法,在这里应该被忽略。并且内部对象 ptrdiff_type_node 对应于我们经常在 C/C++ 程序中看到的 ptrdiff_t 。考虑这个例子:

class base { public : void fb() {} virtual ~base() {} };

class A : public base {};

class B1 : virtual public A {};

class B2 : virtual public A {};

class C: public B1, public B2 {};

int main() {

    C c;

    void (C::*pfn) () = &base::fb;

    (c.*pfn)();

    return 0;

}

语句 PFN pfn = &base::fb; 导致错误 error: pointer to member conversion via virtual base ‘A’ 。但是如果 PFN 被定义为“ typedef void (base::*PFN) (); ”,这就 OK

下面的 binfo_from_vbase 检查指定的 binfo 是否为虚拟基类继承。

 

2525   tree

2526   binfo_from_vbase (tree binfo)                                                                  in search.c

2527   {

2528     for (; binfo; binfo = BINFO_INHERITANCE_CHAIN (binfo))

2529     {

2530       if (TREE_VIA_VIRTUAL (binfo))

2531         return binfo;

2532     }

2533     return NULL_TREE;

2534   }

 

如果 fn 不是应该虚函数,则仅把表达式 fn 转换到方法指针类型。在编译器内部,方法指针的类型看起来就像:

     struct {

       __P __pfn;

       ptrdiff_t __delta;

     };

如果 __pfn NULL ,它是一个空的方法指针。(因为 vtable 总是对象的第一部分,我们不需要其偏移量)。如果该函数是虚函数,那么 PFN 1 加上 2 vtable 索引;否则,它只是一个指向该函数的指针(不幸地,使用 PFN 的最低位,在不强制要求函数地址对齐,或使用这个最低位,例如区分 ISA ,的架构上,不能工作。对于这样的架构,我们使用 DELTA 的最低位,而不是 PFN 的最低位,并且 DELTA 将被乘以 2 )。

因此 5593 行的转换把由 fn 指定的方法地址转变为对应 METHOD_TYPE POINTER_TYPE 。而 delta 将是用于这个方法的 this 指针的调整量。

而如果 fn 是一个虚函数,考虑下面例子:

class A { virtual void f () {} };                                                                      - 1

class B: public A {};

void (B::*pfn) () = &B::f;

 

class A { virtual void f () {} };                                                                      - 2

class B: public A { virtual void f () {} };

void (B::*pfn) () = &B::f;

 

class A { public :                                                                                   - 3

    virtual A* f () { printf ("A::f/n"); return 0; }

    virtual A* f1 () { printf ("A::f1/n"); return 0; }

virtual ~A() {}

};

class B1 : virtual public A { public : virtual B1* f1 () { printf ("B1::f1/n"); return 0; } };

class B2 : virtual public A { public : virtual B2* f () { printf ("B2::f/n"); return 0; } };

class C: public B1, public B2 {};

 

int main() {

    C c;

    B2* (B2::*pfn) () = &C::f;

    (c.*pfn)();

    return 0;

}

注意到上面的 get_delta_difference 获得由 PTRMEM_CST 指定的类与定义该方法的类间的偏移量。在上面的例 1 中,由 PTRMEM_CST 指定的类是 B ,而定义 f 的类是 A ;而在例 2 中,两者都是 B 。那么根据前面章节中类布局的知识,显然,对于这两个例子,类 A 总是被布置在类 B 偏移量为 0 的位置;而对于例 3 delta 则记录了 B2 C 的偏移量。

看到下面的 orig_class fn_class 相同,因此在 560 行的 binfo_or_else 总能成功并返回基类的 binfo (看到不是派生类中的基类部分); 5602 行的 BINFO_OFFSET (binfo) 总是 0 。而 5601 行的 delta 保留着由 get_delta_difference 得到的偏移量。

记得 5607 行的 DECL_VINDEX 被虚函数用来记录它在 vtable 中的索引。因此在 5608 行的 pfn 是这个 vtable 中真正的偏移量,并且很有趣的,看到在 5629 行,这个 pfn 被转换到方法指针类型(当然,它不是最后的地址)。

 

expand_ptrmemfunc_cst (continue)

 

5592     if (!DECL_VIRTUAL_P (fn))

5593       *pfn = convert (TYPE_PTRMEMFUNC_FN_TYPE (type), build_addr_func (fn));

5594     else

5595     {

5596        /* If we're dealing with a virtual function, we have to adjust 'this'

5597         again, to point to the base which provides the vtable entry for

5598         fn; the call will do the opposite adjustment.  */

5599       tree orig_class = DECL_CONTEXT (fn);

5600       tree binfo = binfo_or_else (orig_class, fn_class);

5601       *delta = fold (build (PLUS_EXPR, TREE_TYPE (*delta),

5602                  *delta, BINFO_OFFSET (binfo)));

5603  

5604        /* We set PFN to the vtable offset at which the function can be

5605         found, plus one (unless ptrmemfunc_vbit_in_delta, in which

5606          case delta is shifted left, and then incremented).  */

5607       *pfn = DECL_VINDEX (fn);

5608       *pfn = fold (build (MULT_EXPR, integer_type_node, *pfn,

5609                      TYPE_SIZE_UNIT (vtable_entry_type )));

5610  

5611       switch (TARGET_PTRMEMFUNC_VBIT_LOCATION)

5612       {

5613         case ptrmemfunc_vbit_in_pfn:

5614           *pfn = fold (build (PLUS_EXPR, integer_type_node, *pfn,

5615                          integer_one_node));

5616           break ;

5617  

5618         case ptrmemfunc_vbit_in_delta:

5619           *delta = fold (build (LSHIFT_EXPR, TREE_TYPE (*delta),

5620                           *delta, integer_one_node));

5621           *delta = fold (build (PLUS_EXPR, TREE_TYPE (*delta),

5622                           *delta, integer_one_node));

5623           break ;

5624  

5625         default :

5626           abort ();

5627       }

5628  

5629       *pfn = fold (build1 (NOP_EXPR, TYPE_PTRMEMFUNC_FN_TYPE (type),

5630                        *pfn));

5631     }

5632   }

 

TARGET_PTRMEMFUNC_VBIT_LOCATION 显示在何处保存表示虚函数方法指针的位。它可能是函数地址的最低位;或者它可能是在 this 调整量中的最低位,这依赖于函数地址是否有足够的对齐量。对于 x86 芯片,它使用函数地址的最低位。因此 5629 行的 pfn 事实上是表达式:“ vindex * sizeof (vtable entry) + 1 (在另一个情况下, delta 将是“ BINFO_OFFSET (base) * 2 + 1 带有保存这个虚拟比特的额外空间)。

没在这里显示,这里得到的 delta pfn 将被设置入上面给出的用于方法指针的结构中。

 

59      tree

60      binfo_or_else (tree base, tree type)                                                            in typeck2.c

61      {

62        tree binfo = lookup_base (type, base, ba_ignore, NULL);

63     

64        if (binfo == error_mark_node)

65          return NULL_TREE;

66        else if (!binfo)

67          error_not_base_type (base, type);

68        return binfo;

69      }

 

如我们上面提及的,在构建函数调用的上下文中, pfn 必须是一个方法指针类型的实例,而对于一个正确的函数调用,它必须已经被某个 PTRMEM_CST 初始化,如上面所示。

那么 build_ptrmemfunc_access_expr 用于获取上面提到的域,参数 member_name 指出是哪一个。因此在 pfn_from_ptrmemfunc 中的 5650 行,通过 pfn_identifier 获取了 __pfn 域。

 

2031   tree

2032   build_ptrmemfunc_access_expr (tree ptrmem, tree member_name)                     in typeck.c

2033   {

2034     tree ptrmem_type;

2035     tree member;

2036     tree member_type;

2037  

2038     /* This code is a stripped down version of

2039       build_class_member_access_expr. It does not work to use that

2040       routine directly because it expects the object to be of class

2041       type.  */

2042     ptrmem_type = TREE_TYPE (ptrmem);

2043     my_friendly_assert (TYPE_PTRMEMFUNC_P (ptrmem_type), 20020804);

2044     member = lookup_member (ptrmem_type, member_name, /*protect=*/ 0,

2045                            /*want_type=*/ false);

2046     member_type = cp_build_qualified_type (TREE_TYPE (member),

2047                                       cp_type_quals (ptrmem_type));

2048     return fold (build (COMPONENT_REF, member_type, ptrmem, member));

2049   }

 

然后,上面在 2356 行, e3 正如我们已经看到的,是关于函数地址的表达式(非虚函数指针,或虚函数的 vtable 索引)。接着,它获取 __delta 域入 delta

对于 x86 芯片,在 2362 行的 e1 是测试 __pfn 的最低位是否是 1 的表达式,这是查看该指针是否指向虚函数。在 2363 行,在为虚函数的情形下, idx 被更新为真正的 vtable 偏移量。

 

get_member_function_from_ptrfunc (continue)

 

2357       delta = build_ptrmemfunc_access_expr (function, delta_identifier );

2358       idx = build1 (NOP_EXPR, vtable_index_type , e3);

2359       switch (TARGET_PTRMEMFUNC_VBIT_LOCATION)

2360       {

2361         case ptrmemfunc_vbit_in_pfn:

2362           e1 = cp_build_binary_op (BIT_AND_EXPR, idx, integer_one_node);

2363           idx = cp_build_binary_op (MINUS_EXPR, idx, integer_one_node);

2364           break ;

2365  

2366         case ptrmemfunc_vbit_in_delta:

2367           e1 = cp_build_binary_op (BIT_AND_EXPR, delta, integer_one_node);

2368           delta = cp_build_binary_op (RSHIFT_EXPR, delta, integer_one_node);

2369           break ;

2370  

2371         default :

2372           abort ();

2373       }

2374  

2375        /* Convert down to the right base before using the instance. First

2376         use the type...  */

2377       basetype = TYPE_METHOD_BASETYPE (TREE_TYPE (fntype));

2378       basetype = lookup_base (TREE_TYPE (TREE_TYPE (instance_ptr)),

2379                            basetype, ba_check, NULL);

2380       instance_ptr = build_base_path (PLUS_EXPR, instance_ptr, basetype, 1);

2381       if (instance_ptr == error_mark_node)

2382         return error_mark_node;

 

注意 instance_ptr 是一个指针,那么 instance_ptr TREE_TYPE TREE_TYPE 返回这个对象的 RECORD_TYPE ,而 TYPE_METHOD_BASETYPE 返回声明该函数的基类。看到与 instance_ptr 关联的类及与该函数关联的类必须具有派生关系。

函数 build_base_path 转换自 / 至一个基类子对象。参数 expr 是一个具有类型‘ A ’或‘ A* ’的表达式,返回一个具有‘ B ’或‘ B* ’的表达式。把 A 转换到一个基类 B code PLUS_EXPR binfo A B 基类实例的 binfo 。把基类 A 转换到派生类 B code MINUS_EXPR binfo B A 实例的 binfo 。在后一个情形中, A 必须不是 B 一个确实的虚拟基类。参数 nonnull true ,如果 expr 已知是非空的(当 expr 具有指针类型,这才需要)。 CV 限定从 expr 保留。考虑下面的例子:

        class A { public : void fa (); };                                                                      - 4

        class B: public A { public : void fb(); };

        class C:: public B {};

        void (B::*pfn) = &B::f;

C c;

(c.*pfn) ();

对于这个例子, fntype pfn 的类型,因此 2377 行的 basetype B (与该方法指针绑定的类型);而 instance_ptr 是类型 C 。需要调整 instance_ptr 到基类 B 来执行语句“ (c.*pfn)(); ”。

下面的 build_base_path 构建进行这样转换的表达式。在这个函数中, instance_ptr 被传入做参数 expr ,它是一个指针,因此下面的 want_pointer 是非 0 值。显然参数 nonnull 也是非 0 。然后 v_binfo 指向最接近 binfo 的虚拟派生类,而 d_binfo ,通过 261 行的 FOR 循环,记录了最后派生类。注意到 binfo 从由 expr 的类型为根节点的继承树中获得,对于 PLUS_EXPR ,必然满足 272 行的断言。

 

242    tree

243    build_base_path (enum tree_code code,                                                     in class.c

244                   tree expr,

245                   tree binfo,

246                   int nonnull)

247    {

248      tree v_binfo = NULL_TREE;

249      tree d_binfo = NULL_TREE;

250      tree probe;

251      tree offset;

252      tree target_type;

253      tree null_test = NULL;

254      tree ptr_target_type;

255      int fixed_type_p;

256      int want_pointer = TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE;

257   

258      if (expr == error_mark_node || binfo == error_mark_node || !binfo)

259        return error_mark_node;

260   

261      for (probe = binfo; probe; probe = BINFO_INHERITANCE_CHAIN (probe))

262      {

263        d_binfo = probe;

264        if (!v_binfo && TREE_VIA_VIRTUAL (probe))

265          v_binfo = probe;

266      }

267   

268      probe = TYPE_MAIN_VARIANT (TREE_TYPE (expr));

269      if (want_pointer)

270        probe = TYPE_MAIN_VARIANT (TREE_TYPE (probe));

271     

272      my_friendly_assert (code == MINUS_EXPR

273                       ? same_type_p (BINFO_TYPE (binfo), probe)

274                       : code == PLUS_EXPR

275                         ? same_type_p (BINFO_TYPE (d_binfo), probe)

276                         : false, 20010723);

277     

278      if (code == MINUS_EXPR && v_binfo)

279      {

280        error ("cannot convert from base `%T' to derived type `%T' via virtual base `%T'",

281              BINFO_TYPE (binfo), BINFO_TYPE (d_binfo), BINFO_TYPE (v_binfo));

282        return error_mark_node;

283      }

284   

285      if (!want_pointer)

286        /* This must happen before the call to save_expr.  */

287        expr = build_unary_op (ADDR_EXPR, expr, 0);

288   

289      fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);

290      if (fixed_type_p <= 0 && TREE_SIDE_EFFECTS (expr))

291        expr = save_expr (expr);

 

在调用下面的函数之前,如果 want_pointer 0 ,表示 expr 不是指针。把 expr 封装入一个 ADDR_EXPR 节点,它将用下面的函数把 nonnull 强制为非 0 值。注意到 5410 行的 t instance 的静态类型( static type ),但它可能与真正类型不完全一样(动态类型)。考虑下面的例子,它将给出与例 3 一样的结果:

class A { public :                                                                                   - 5

    virtual A* f () { printf ("A::f/n"); return 0; }

    virtual A* f1 () { printf ("A::f1/n"); return 0; }

virtual ~A() {}

};

class B1 : virtual public A { public : virtual B1* f1 () { printf ("B1::f1/n"); return 0; } };

class B2 : virtual public A { public : virtual B2* f () { printf ("B2::f/n"); return 0; } };

class C: public B1, public B2 { };

 

int main() {

    A *pa = new C;

    A* (A::*pfn) () = &A::f;

    (pa->*pfn)();

    delete pa;

    return 0;

}

那么对于 pa ,其静态类型是“ A* ”,如它被声明的那样,而其动态类型是“ C* ”,它在运行时由 new 语句确定。注意到 pfn 最后调用 B2::f ,因为虚函数机制。

函数 resolves_to_fixed_type_p 返回非 0 值,如果静态类型与动态类型相同(而如果 instance 还是构造函数 / 析构函数,则返回负数)。

 

5407   int

5408   resolves_to_fixed_type_p (tree instance, int* nonnull)                                 in class.c

5409   {

5410     tree t = TREE_TYPE (instance);

5411     int cdtorp = 0;

5412    

5413     tree fixed = fixed_type_or_null (instance, nonnull, &cdtorp);

5414     if (fixed == NULL_TREE)

5415       return 0;

5416     if (POINTER_TYPE_P (t))

5417       t = TREE_TYPE (t);

5418     if (!same_type_ignoring_top_level_qualifiers_p (t, fixed))

5419       return 0;

5420     return cdtorp ? -1 : 1;

5421   }

 

instance 的动态类型如果已知,由 fixed_type_or_null 返回, 其中参数 nonnull 被设置,当且仅当 instance 明确知道是非空的,不管对其类型 所知多少;而参数 cdtorp 是非 0 值,如果 instance 还是‘ this ’指针的表达式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值