zoukankan      html  css  js  c++  java
  • C++构造函数初始化相关操作

    一、构造函数
    构造函数在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.0gcccpparser.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);
        }
    }

  • 相关阅读:
    int func(int aa[4]) { return sizeof(aa);}
    C语言中的undefined behavior系列(3) trap representation
    面试被轮及访博客园三人组蹭饭记 20100910
    关于带新人和作新人。
    [C++]Call virtual member function in constructor or destructor
    The using declaration in C++
    WF & WCF(5)
    sqlserver 常用函數
    WF & WCF(4)
    oracle 三種集合類型比較(indexby表,嵌套表,數組)
  • 原文地址:https://www.cnblogs.com/tsecer/p/10487487.html
Copyright © 2011-2022 走看看