注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

C++构造函数初始化相关操作  

2013-02-03 12:56:40|  分类: Gcc源代码分析 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、构造函数
构造函数在C++中扮演着基础性的功能,再加上成员的初始化列表,问题就变的更加有意思的。通常也是机械性的写类和对应的构造函数,然后再对成员在初始化列表中对必要的成员进行初始化操作,例如对于一些整数类型赋值为非法初始值,以区分和识别一些未初始化的变量。但是在大部分情况下,我们并煤油灯对于一些复杂的结构进行过初始化,例如我们最为常用的vector结构,map结构,很多人—例如我—根本没有在意过这些STL标准库的容量管理对象的构造函数是什么样子,它可以使用多少个参数等这些信息。
有时候看着一个类的构造函数,突然会感觉到有些别扭,就像你无聊的时候单独看一个汉字,看着看着就觉得自己不再认识这个汉字了一样。对于构造函数,有些我们关心的成员在初始化列表中进行了初始化,但是更多的成员我们对没有在意它是如何进行了,例如成员中的vector<somethin>,那么我们需要在构造函数的函数体内对这个成员进行clear来清除它可能存在的参与信息吗?如果不是,它的构造函数在什么时候完成,执行了什么样的操作?
通常的时候我们更愿意通过一个简单的test来测试这个程序的行为,然后得出一个结论,完成了代码,至少完成了我们的需求。只是这样简单测试的结果只是说明了这种情况下的问题,例如这个厂商的这一款编译器的这个版本是这个行为,而没有保证这个结果有多大程度上的可信赖性。虽然法律或者道德的约束在很多情况下没有那么强烈,但是师出有名、名正言顺在所有的事情中都可以先天得到一些舆论和心理上的优势。就像大家经常说的,你可以这么做,但是要知道这样做是错误的。所以我还是看了一下C++的说明手册,在找了一下标准中对于该内容的说明。
二、C++中对于该内容的说明
以下内容来自《ANSI C++ Reference》中对于初始化的相关说明
12.6.2 Initializing bases and members [class.base.init]
3 The expressionlist in a meminitializer is used to initialize the base class or nonstatic data member subobject denoted by the meminitializerid.The semantics of a meminitializer are as follows:
— if the expression list of the meminitializer is omitted, the base class or member subobject is default initialized (see 8.5);
— otherwise, the subobject indicated by meminitializerid is directinitialized using expressionlist as the initializer (see 8.5).
……
Initialization shall proceed in the following order:
— First, and only for the constructor of the most derived class as described below, virtual base classes shall be initialized in the order they appear on a depthfirst lefttoright traversal of the directed acyclic graph of base classes, where “lefttoright” is the order of appearance of the base class names in the derived class basespecifierlist.
— Then, direct base classes shall be initialized in declaration order as they appear in the basespecifierlist (regardless of the order of the meminitializers).
— Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the meminitializers).
— Finally, the body of the constructor is executed.[Note: the declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. ]
三、gcc中对于相关内容的处理
1、构造及初始化子操作
gcc-4.1.0\gcc\cp\parser.c
cp_parser_mem_initializer_list--->>>finish_mem_initializers--->>>emit_mem_initializers---->>>sort_mem_initializers

/* The MEM_INITS are a TREE_LIST.  The TREE_PURPOSE of each list gives
   a FIELD_DECL or BINFO in T that needs initialization.  The
   TREE_VALUE gives the initializer, or list of initializer arguments.

   Return a TREE_LIST containing all of the initializations required
   for T, in the order in which they should be performed.  The output
   list has the same format as the input.  */

static tree
sort_mem_initializers (tree t, tree mem_inits)
{
  tree init;
  tree base, binfo, base_binfo;
  tree sorted_inits;
  tree next_subobject;
  VEC(tree,gc) *vbases;
  int i;
  int uses_unions_p;

  /* Build up a list of initializations.  The TREE_PURPOSE of entry
     will be the subobject (a FIELD_DECL or BINFO) to initialize.  The
     TREE_VALUE will be the constructor arguments, or NULL if no
     explicit initialization was provided.  */
  sorted_inits = NULL_TREE;

  /* Process the virtual bases.  */
  for (vbases = CLASSTYPE_VBASECLASSES (t), i = 0;
       VEC_iterate (tree, vbases, i, base); i++)
    sorted_inits = tree_cons (base, NULL_TREE, sorted_inits);

  /* Process the direct bases.  */
  for (binfo = TYPE_BINFO (t), i = 0;
       BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
    if (!BINFO_VIRTUAL_P (base_binfo))
      sorted_inits = tree_cons (base_binfo, NULL_TREE, sorted_inits);

  /* Process the non-static data members.  */
  sorted_inits = build_field_list (t, sorted_inits, &uses_unions_p);
  /* Reverse the entire list of initializations, so that they are in
     the order that they will actually be performed
.  */
  sorted_inits = nreverse (sorted_inits);

  /* If the user presented the initializers in an order different from
     that in which they will actually occur, we issue a warning.  Keep
     track of the next subobject which can be explicitly initialized
     without issuing a warning.  */
  next_subobject = sorted_inits;

  /* Go through the explicit initializers, filling in TREE_PURPOSE in
     the SORTED_INITS.  */
  for (init = mem_inits; init; init = TREE_CHAIN (init))
    {
      tree subobject;
      tree subobject_init;

      subobject = TREE_PURPOSE (init);

      /* If the explicit initializers are in sorted order, then
     SUBOBJECT will be NEXT_SUBOBJECT, or something following
     it.  */
      for (subobject_init = next_subobject;
       subobject_init;
       subobject_init = TREE_CHAIN (subobject_init))
    if (TREE_PURPOSE (subobject_init) == subobject)
      break;

      /* Issue a warning if the explicit initializer order does not
     match that which will actually occur.
     ??? Are all these on the correct lines?  */
      if (warn_reorder && !subobject_init)
    {
      if (TREE_CODE (TREE_PURPOSE (next_subobject)) == FIELD_DECL)
        warning (0, "%q+D will be initialized after",
             TREE_PURPOSE (next_subobject));
      else
        warning (0, "base %qT will be initialized after",
             TREE_PURPOSE (next_subobject));
      if (TREE_CODE (subobject) == FIELD_DECL)
        warning (0, "  %q+#D", subobject);
      else
        warning (0, "  base %qT", subobject);
      warning (0, "%J  when initialized here", current_function_decl); 告警提示成员布局顺序和初始化顺序不一致
    }

      /* Look again, from the beginning of the list.  */
      if (!subobject_init)
    {
      subobject_init = sorted_inits;
      while (TREE_PURPOSE (subobject_init) != subobject)
        subobject_init = TREE_CHAIN (subobject_init);
    }

      /* It is invalid to initialize the same subobject more than
     once.  */
      if (TREE_VALUE (subobject_init))
    {
      if (TREE_CODE (subobject) == FIELD_DECL)
        error ("%Jmultiple initializations given for %qD",
           current_function_decl, subobject);
      else
        error ("%Jmultiple initializations given for base %qT",
           current_function_decl, subobject);
    }

      /* Record the initialization.  */
      TREE_VALUE (subobject_init) = TREE_VALUE (init);使用meminitializer提供的构造函数来初始化该成员,相当于初始化操作替换
      next_subobject = subobject_init;
    }

  /* [class.base.init]

     If a ctor-initializer specifies more than one mem-initializer for
     multiple members of the same union (including members of
     anonymous unions), the ctor-initializer is ill-formed.  */
  if (uses_unions_p) 如果有初始化子对于同一个union成员的多个成员提供了初始化操作,提示错误
    {
      tree last_field = NULL_TREE;
      for (init = sorted_inits; init; init = TREE_CHAIN (init))
    {
      tree field;
      tree field_type;
      int done;

      /* Skip uninitialized members and base classes.  */
      if (!TREE_VALUE (init)
          || TREE_CODE (TREE_PURPOSE (init)) != FIELD_DECL)
        continue;
      /* See if this field is a member of a union, or a member of a
         structure contained in a union, etc.  */
      field = TREE_PURPOSE (init);
      for (field_type = DECL_CONTEXT (field);
           !same_type_p (field_type, t);
           field_type = TYPE_CONTEXT (field_type))
        if (TREE_CODE (field_type) == UNION_TYPE)
          break;
      /* If this field is not a member of a union, skip it.  */
      if (TREE_CODE (field_type) != UNION_TYPE)
        continue;

      /* It's only an error if we have two initializers for the same
         union type.  */
      if (!last_field)
        {
          last_field = field;
          continue;
        }

      /* See if LAST_FIELD and the field initialized by INIT are
         members of the same union.  If so, there's a problem,
         unless they're actually members of the same structure
         which is itself a member of a union.  For example, given:

           union { struct { int i; int j; }; };

         initializing both `i' and `j' makes sense.  */
      field_type = DECL_CONTEXT (field);
      done = 0;
      do
        {
          tree last_field_type;

          last_field_type = DECL_CONTEXT (last_field);
          while (1)
        {
          if (same_type_p (last_field_type, field_type))
            {
              if (TREE_CODE (field_type) == UNION_TYPE)
            error ("%Jinitializations for multiple members of %qT",
                   current_function_decl, last_field_type);
              done = 1;
              break;
            }

          if (same_type_p (last_field_type, t))
            break;

          last_field_type = TYPE_CONTEXT (last_field_type);
        }

          /* If we've reached the outermost class, then we're
         done.  */
          if (same_type_p (field_type, t))
        break;

          field_type = TYPE_CONTEXT (field_type);
        }
      while (!done);

      last_field = field;
    }
    }

  return sorted_inits;
}
2、是否需要执行构造的判断

/* Check the validity of the bases and members declared in T.  Add any
   implicitly-generated functions (like copy-constructors and
   assignment operators).  Compute various flag bits (like
   CLASSTYPE_NON_POD_T) for T.  This routine works purely at the C++
   level: i.e., independently of the ABI in use.  */

static void
check_bases_and_members (tree t)
{
  /* Nonzero if the implicitly generated copy constructor should take
     a non-const reference argument.  */
  int cant_have_const_ctor;
  /* Nonzero if the implicitly generated assignment operator
     should take a non-const reference argument.  */
  int no_const_asn_ref;
  tree access_decls;

  /* By default, we use const reference arguments and generate default
     constructors.  */
  cant_have_const_ctor = 0;
  no_const_asn_ref = 0;

  /* Check all the base-classes.  */
  check_bases (t, &cant_have_const_ctor,
           &no_const_asn_ref);

  /* Check all the method declarations.  */
  check_methods (t);

  /* Check all the data member declarations.  We cannot call
     check_field_decls until we have called check_bases check_methods,
     as check_field_decls depends on TYPE_HAS_NONTRIVIAL_DESTRUCTOR
     being set appropriately.  */
  check_field_decls (t, &access_decls,
             &cant_have_const_ctor,
             &no_const_asn_ref);

  /* A nearly-empty class has to be vptr-containing; a nearly empty
     class contains just a vptr.  */
  if (!TYPE_CONTAINS_VPTR_P (t))
    CLASSTYPE_NEARLY_EMPTY_P (t) = 0;

  /* Do some bookkeeping that will guide the generation of implicitly
     declared member functions.  */
  TYPE_HAS_COMPLEX_INIT_REF (t)
    |= (TYPE_HAS_INIT_REF (t) || TYPE_CONTAINS_VPTR_P (t));
  TYPE_NEEDS_CONSTRUCTING (t)
    |= (TYPE_HAS_CONSTRUCTOR (t) || TYPE_CONTAINS_VPTR_P (t)
); 如果有构造函数或者包含了虚函数指针,因为对象的虚函数指针也是在构造函数中执行初始化的
  CLASSTYPE_NON_AGGREGATE (t)
    |= (TYPE_HAS_CONSTRUCTOR (t) || TYPE_POLYMORPHIC_P (t));
  CLASSTYPE_NON_POD_P (t)
    |= (CLASSTYPE_NON_AGGREGATE (t)
    || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
    || TYPE_HAS_ASSIGN_REF (t));
  TYPE_HAS_COMPLEX_ASSIGN_REF (t)
    |= TYPE_HAS_ASSIGN_REF (t) || TYPE_CONTAINS_VPTR_P (t);

  /* Synthesize any needed methods.  */
  add_implicitly_declared_members (t,
                   cant_have_const_ctor,
                   no_const_asn_ref);

  /* Create the in-charge and not-in-charge variants of constructors
     and destructors.  */
  clone_constructors_and_destructors (t);

  /* Process the using-declarations.  */
  for (; access_decls; access_decls = TREE_CHAIN (access_decls))
    handle_using_decl (TREE_VALUE (access_decls), t);

  /* Build and sort the CLASSTYPE_METHOD_VEC.  */
  finish_struct_methods (t);

  /* Figure out whether or not we will need a cookie when dynamically
     allocating an array of this type.  */
  TYPE_LANG_SPECIFIC (t)->u.c.vec_new_uses_cookie
    = type_requires_array_cookie (t);
}
3、数组是否需要构造函数依赖于它包含的对象是否需要构造函数:
int
cp_complete_array_type (tree *ptype, tree initial_value, bool do_default)
{
……
  /* We can create the array before the element type is complete, which
     means that we didn't have these two bits set in the original type
     either.  In completing the type, we are expected to propagate these
     bits.  See also complete_type which does the same thing for arrays
     of fixed size.  */
  type = *ptype;
  if (TYPE_DOMAIN (type))
    {
      elt_type = TREE_TYPE (type);
      TYPE_NEEDS_CONSTRUCTING (type) = TYPE_NEEDS_CONSTRUCTING (elt_type);其中elt_type表示数组element的类型
      TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)
    = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (elt_type);
    }
}
  评论这张
 
阅读(1448)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017