精华内容
下载资源
问答
  • 一般我们进行成员变量初始化用两种方法 第一种是通过在构造函数内赋值 class Point { public:  Point(){ _x = 0; _y = 0;};  Point( int x, int y ){ _x = 0; _y = 0; } private:  int _x, _y; }; ...

    一般我们进行成员变量初始化用两种方法

    第一种是通过在构造函数内赋值

    class Point
    {
    public:
     Point(){ _x = 0; _y = 0;};
     Point( int x, int y ){ _x = 0; _y = 0; }
    private:
     int _x, _y;
    };

    第二种是使用初始化列表

      class Point
    {
    public:
     Point():_x(0),_y(0){};
     Point( int x, int y ):_x(x),_y(y){}
    private:
     int _x, _y;
    };

     

    这两种用法是有区别的

    一、在有些情况下,必须使用初始化列表。特别是const和引用数据成员被初始化时。

    class Point
    {
    // 这个类的定义就要求使用初始化成员列表,因为const成员只能被初始化,不能被赋值
    public:
     Point():_x(0),_y(0){}; 
     Point( int x, int y ):_x(x),_y(y){}
     //Point(){ _x = 0; _y = 0;}
     //Point( int x, int y ){ _x = 0; _y = 0; }
    private:
     const int _x, _y;
    };

     

    二、是从效率方面来说的,对于内置类型或复合类型,差异不会太大,但对于非内置数据类型,差异还是很明显的

    如我们再给Point类添加一个新的string类型的成员变量

    class Point
    {
     const int _x, _y;
     string _name;
    };

     

    构造函数内赋值进行初始化

    Point( int x, int y, string name ){ _x = 0; _y = 0; _name = name; }

     

    _name = name 这个表达式会调用string类的缺省构造函数一次,再调用Operator=函数进行赋值一次。所以需调用两次函数:一次构造,一次赋值

     

    用初始化列表进行初始化

    Point( int x, int y, string name ):_x(x),_y(y), _name(name){}  

    _name会通过拷贝构造函数仅以一个函数调用的代码完成初始化

    即使是一个很简单的string类型,不必要的函数调用也会造成很高的代价。随着类越来越大,越来越复杂,它们的构造函数也越来越大而复杂,那么对象创建的代价也越来越高,所以一般情况下建议使用初始化列表进行初始化,不但可以满足const和引用成员的初始化要求,还可以避免低效的初始化数据成员。

    详细出处参考:http://www.itqun.net/content-detail/105800_2.html

    ————————————————————————————————————————————————————————

    C++构造函数中初始化成员__参数列表初始化成员(必须用原因:对象成员的初始化,const修饰的成员的初始化,引用成员的初始化,子类调用父类的构造函数初始化父类成员)__参数列表在构造函数执行之前执行,参数列表中执行的是初始化(所有的成员,无论是否出现在参数列表中,都会有初始化),参数列表的执行顺序与类中成员的声明顺序,与类的继承顺序相一致__构造函数中执行的一般是赋值_多重继承,虚继承构造函数的参数初始化列表的区别  

    类对象的构造顺序是这样的:
    1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员;
    2.进入构造函数后在构造函数中执行一般赋值与计算。

    使用初始化列表有两个原因:
    原因1.必须这样做:

    《C++ Primer》中提到在以下三种情况下需要使用初始化成员列表:

    一、需要初始化的数据成员是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化);
    二、需要初始化const修饰的类成员;
    三、需要初始化引用成员数据;

    即:

    例一、数据成员是对象,切对象只有含参数的构造函数;
            如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。
    class Test
    {
    public:
             Test(int x,int y,int z);
    private:
             int a;
             int b;
             int c;
    };
    class MyTest
    {
    public:
            MyTest():test(1,2,3){}        //初始化,初始化列表在构造函数执行前执行(这个可以测试,对同一个变量在初始化列表和构造函数中分别初始化,首先执行参数列表,后在函数体内赋值,后者会覆盖前者)。
    private:
            Test test;            //声明
    };

            因为Test有了显示的带参数的构造函数,那么他是无法依靠编译器生成无参构造函数的,所以没有三个int型数据,就无法创建Test的对象。
            Test类对象是MyTest的成员,想要初始化这个对象test,那就只能用成员初始化列表,没有其他办法将参数传递给Test类构造函数

    例二、对象引用或者cosnt修饰的数据成员
            另一种情况是这样的:类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的

    即:

    class Test

    {

         priate:

              const int a;               //const成员声明

          public:

              Test():a(10){}           //初始化

    };

    class Test

    {

        private:

             int &a;                        //声明

        public:

             Test(int a):a(a){}        //初始化

    }

    例三、子类初始化父类的私有成员,需要在(并且也只能在)参数初始化列表中显示调用父类的构造函数,如下:

     

    class Test {  private:   int a;   int b;   int c;  public:   Test(int a,int b,int c)   {    this->a = a;    this->b = b;    this->c = c;   }   int getA(){return a;}   int getB(){return b;}   int getC(){return c;} };

    class MyTest:public Test {  private:   int d;  public:   MyTest(int a,int b,int c,int d):Test(a,b,c)   //MyTest(int a,int b,int c,int d)   {       //Test(a,b,c);           //构造函数只能在初始化列表中被显示调用,不能在构造函数内部被显示调用    this->d = d;   }   int getD(){return d;} };

     

    int main(int argc,char *argv[]) {  MyTest mytest(1,2,3,4);  printf("a=%d,b=%d,c=%d,d=%d\n",    mytest.getA(),mytest.getB(),mytest.getC(),mytest.getD());  return 0; }

     
    多重继承,虚继承构造函数的参数初始化列表的区别:
    代码如下:
       
    注意多重继承子类的构造函数
    #include   <stdio.h> class   CTop { private : int  a ; public : int  getA () { return  a ; } CTop ( int  a ) { this -> a   =  a ; } }; class   CLeft : public CTop { private : int  b ; public : int  getL () { return  b ; } CLeft ( int  a , int  b ): CTop ( a ) { this -> =  b ; } }; class   CRight : public CTop { private : int  c ; public : int  getR () { return  c ; } CRight ( int  a , int  c ): CTop ( a ) { this -> =  c ; } }; class   Test : public   CLeft , public   CRight { private : int  d ; public : int  getT () { return  d ; } Test ( int  a , int  b , int  c , int  d ): CLeft(a,b),CRight(a,c) { this -> =  d ; } }; int  main ( int  argc , char   * argv []) { Test  obj ( 1 , 2 , 3 , 4 ); printf ( "obj.a=%d,obj.b=%d,obj.c=%d,obj.d=%d\n" , obj . CLeft :: getA (), obj . getL (), obj . getR (), obj . getT ()); //getA有歧义,要用类名来做区分。 return   0 ; }
     
     

    注意虚继承子类的构造函数

    #include <stdio.h> class CTop { private : int a ; public : int getA () { return a ; } CTop ( int a ) { this -> a = a ; } }; class CLeft : virtual public CTop
    { private : int b ; public : int getL () { return b ; } CLeft ( int a , int b ): CTop ( a ) { this -> b = b ; } }; class CRight : virtual public CTop { private : int c ; public : int getR () { return c ; } CRight ( int a , int c ): CTop ( a ) { this -> c = c ; } }; class Test : public CLeft,public CRight { private : int d ; public : int getT () { return d ; } Test ( int a , int b , int c , int d ): CLeft(a,b),CRight(a,c), CTop(a) { this -> d = d ; } }; int main ( int argc , char * argv []) { Test obj ( 1 , 2 , 3 , 4 ); printf ( "obj.a=%d,obj.b=%d,obj.c=%d,obj.d=%d\n" , obj . getA (), obj . getL (), obj . getR (), obj . getT ()); //因为采用虚基类,虚继承机制保证了a只有一份,所以不存在歧义。 return 0 ; }

     

    原因2.效率要求这样做:
           类对象的构造顺序显示,进入构造函数体后,进行的是计算,是对成员变量的赋值操作,显然,赋值和初始化是不同的,这样就体现出了效率差异,如果不用成员初始化类表,那么类对自己的类成员分别进行的是一次隐式的默认构造函数的调用,和一次赋值操作符的调用,如果是类对象,这样做效率就得不到保障。

    注意:构造函数需要初始化的数据成员,不论是否显示的出现在构造函数的成员初始化列表中,都会在该处完成初始化,并且初始化的顺序和其在类中声明时的顺序是一致的,与列表的先后顺序无关所以要特别注意,保证两者顺序一致才能真正保证其效率和准确性

    为了说明清楚,假设有这样一个类:
    class foo
    {
          private :
              int a, b;
    };
    1、foo(){}和foo(int i = 0){}都被认为是默认构造函数,因为后者是默认参数。两者不能同时出现。
    2、构造函数列表的初始化方式不是按照列表的的顺序,而是按照变量声明的顺序。比如foo里面,a在b之前,那么会先构造a再构造b。所以无论foo():a(b + 1), b(2){}还是foo():b(2),a(b+1){}都不会让a得到期望的值。

    3、构造函数列表能够对const成员初始化。比如foo里面有一个int const c;则foo(int x) : c(x){}可以让c值赋成x。

    不过需要注意的是,c必须在每个构造函数(如果有多个)都有值。
    4、在继承里面,只有初始化列表可以构造父类的private成员(通过显示调用父类的构造函数)。比如说:
           class child : public foo

    {
           };
           foo里面的构造函数是这样写的:

    foo (int x)

    {

           a = x;

    }.
           而在child里面写child(int x){ foo(x); }是通过不了编译的。

    只有把子类构造函数写作child (int x) : foo(x){}才可以。

    ——————————————————————————————————————————————————————————————————
    关于复合类型:

    C++中,什么是复合类型?

    刚开始,还以为自定义的类是复合类型。查了C++ primer才知道复合类型不是类。

    在C++中类型大致可以分为三种

    一、内置类型

    如int, char, float, unsigned等。内置类型是最基本的类型。

    二、复合类型

    复合类型:使用其它类型定义的类型。有三种复合类型:引用,指针,数组。

    三、类类型

    就是类。比如string以及自己定义的类。


    数组、结 构和指针式c++3种复合类型。

    数组,可以在一个数据对象中存储多个同种类型的值。通过使用索引或下标,可以访问数组中的各个元素。

    结构,可以将多个不同类型的值存储在同一个数据对象中,可以使用成员关系操作符(.来访问其中的成员。使用结构的第一步是创建结构模板,它定义了结构存储了哪些成员。模板的名称将成为新类型的标识符,然后就可以声明这种类型的结构变量。

    共用体,可以存储一个值,但是这个值可以是不同的类型,成员名指出了使用的模式。

    指针,是被设计用来存储地址的变量的。我们说,指针指向它存储的地址。指针声明指出了指针指向的对象的类型。对指针应用解除引用操作符,将得到指针指向的位置中的值

    字符串,是以空字符为结尾的一系列字符。字符串可用引号括起来的字符串常量表示,其中隐式包含了结构的空字符。可以将字符串存储在char数组中,可以用被初始化为指向字符串的 char指针表示字符串。函数strlen()返回字符串的长度,其中不包括空字符。函数strcpy()将字符串从一个位置复制到另一个位置。在使用这些函数时,应包含头文件cstringstring.h.

    头文件string支持 C++string类提供了另一种对用户更为友好的字符串处理方法。具体地说,string对象将根据要存储的字符串自动调整其大小,用户可以使用赋值操作符来复制字符串。

    New操作符允许在程序运行时为数据对象请求内存。该操作符返回其内存的地址,可以将这个地址赋给一个指针,程序将只能使用该指针来访问内存。如果数据对象是简单变量,则可以使用解除引用操作符(*)来获得其值;如果数据对象是数组,则可以像使用数组名那样来使用指针来访问元素;如果数据对象是结构,则可以用指针解除引用操作符(->) 来访问其成员。

    指针和数 组紧密相关。如果ar是数组名,则表达式ar[i]被解释为*(ar+i),其中数组名被解释为数组第一个元素的地址。这样,数组名的作用和指针相同。反过来,可以使用数组表示法,通过指针名来访问new分配的数组中的元素。

    操作符newdelete  允许显式控制何时给数据对象分配内存,何时将内存归给内存池。自动变量是在函数中声明的变量,而静态变量时在函数外部或者使用关键字static声明的变 量,这两种变量都不太灵活。自动变量在程序执行到其所属的代码块(通常是函数定义)时产生,在离开该代码块时终止。静态变量在整个程序周期内部都存在。


    展开全文
  • 例如:构建一个maxrownum*maxrownum的矩阵  long double **data1_bfE;    data1_bfE=(long double **)malloc(maxrownum*sizeof(long double *));    for(i=0;imaxrownum;... data1_

        例如:构建一个maxrownum*maxrownum的矩阵


        long double **data1_bfE;

        

        data1_bfE=(long double **)malloc(maxrownum*sizeof(long double *));

     

        for(i=0;i<maxrownum;i++)

        {

            data1_bfE[i]=(long double *)malloc(maxrownum*sizeof(*data1_bfE));

        }


      
    data1_bfE[i]=(long double *)malloc(maxrownum*sizeof(*data1_bfE));
    动态分配maxrownumlong double的内存空间,跟long double a[5]相似,但前者是堆上分配的,后者是栈上分配的;

        

    data1_bfE=(long double **)malloc(maxrownum*sizeof(long double *));
    意思就是定义一个有maxrownum个long double类型的指针的动态数组,然后在里面存放指针,相当于容器,data1_bfE就会成为指针的指针。

    展开全文
  • javac 中的if-else变量初始化问题

    万次阅读 2018-09-27 17:22:56
    在前几篇文章中我们介绍过了AbstractAssignAnalyzer.visitIf(JCIf),该方法是用来判断在if语句中变量初始化情况的.例如如下代码: public void assgin(){ int c; int a = 0; if(c != 0){ } } 在IDE ...

    前言

    在前几篇文章中我们介绍过了AbstractAssignAnalyzer.visitIf(JCIf),该方法是用来判断在if语句中变量的初始化情况的.例如如下代码:

    public void assgin(){
    		
    		int c;
    		int a = 0;
    		if(c != 0){
    
    		}	
    	}
    

    在IDE 中会提示 The local variable c may not have been initialized,那么它是如何检测的呢?

    又如如下代码:

    public void assgin(){
    	
    	int c;
    	int a = 0;
    	if((c = 1) != c){
    
    	}
    	
    	
    }
    

    我们可以通过看代码的方式知道,变量a,c都已初始化了,变量值分别是0,1,那么AbstractAssignAnalyzer.visitIf方法是判断在if语句中变量的初始化情况的.那么该方法又是如何检测的呢?接下来我们就案例来进行讲解

    解析

    变量未初始化问题

    本案例的测试代码如下:

    public class IfAssign {
    
    	public void assgin1(){
    		
    		int c;
    		int a = 0;
    		if(c != 0 ){
    		}
    		
    	}
    	
    }
    

    还是在javac中的Flow阶段,最终来到了AbstractAssignAnalyzer.analyzeTree(Env<?>, JCTree)方法,在该方法中,又调用了Flow.BaseAnalyzer.scan(JCTree)方法,此时由于IfAssign中没有静态变量,实例变量,静态初始化块,实例初始化块,只有两个方法(其中一个是编辑器自动插入的<init> 方法).因此,最终会执行如下代码:

    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
        if (l.head.hasTag(METHODDEF)) {
            scan(l.head);
        }
    }
    

    此时IfAssign所对应的代码如下:

    public class IfAssign {
        
        public IfAssign() {
            super();
        }
        
        public void assgin1() {
            int c;
            int a = 0;
            if (c != 0) {
            }
        }
        
    }
    

    至于为何<init>方法声明在assgin1方法之前,这点我们之后进行讲解,这个不是本文重点,故不进行讲解

    因此,该循环第一遍处理的是<init>方法,这个不是本文重点,不进行讲解.

    第二遍的时候,处理assgin1方法,由于方法对应的tree节点是JCMethodDecl.因此会最终调用AssignAnalyzer.visitMethodDef(JCMethodDecl)方法.

    对于assgin1方法所对应的树如下:

    1

    那么在该方法的处理过程就是对这个树的先序遍历.

    那么在AssignAnalyzer#visitMethodDef方法中,是进行判断,保存现场,参数处理,这部分的内容在

    BaseAnalyzer,AbstractAssignAnalyzer,AssignAnalyzer解析 中介绍,本文就不在介绍了.

    我们重点看对方法体的处理:

    scan(tree.body);
    

    由于JCMethodDecl的body对应的是JCBlock,因此会调用AbstractAssignAnalyzer.visitBlock(JCBlock).如下:

    public void visitBlock(JCBlock tree) {
        int nextadrPrev = nextadr;
        scan(tree.stats);
        nextadr = nextadrPrev;
    }
    

    在该方法中首先保存了nextadr,然后处理完JCBlock持有的stats后,再恢复nextadr.这是为啥呢?设想如下情况:

    public void test(int b){
    
    	int a = 0;
    	if(a != b){
    		int c;
    	}else{
    		int d;
    	}
    }
    

    对于这个代码,c,d应该都是属于同一个本地变量表中的位置.那么在visitBlock方法中的保存,恢复的操作就是保证这个情况下变量位置的。

    接下来,调用的是TreeScanner.scan(List<? extends JCTree>),代码如下:

    public void scan(List<? extends JCTree> trees) {
        if (trees != null)
        for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail)
            scan(l.head);
    }
    

    由于当前处理的是assgin1方法中的代码块,在该块中包含3条语句:

    • 2条变量声明语句

    • if 语句

    因此,这里的循环会执行3次.

    • 第一次执行(int c)如下:

      由于变量语句对应的节点为JCVariableDecl,因此,会执行AbstractAssignAnalyzer.visitVarDef(JCVariableDecl),如下:

      public void visitVarDef(JCVariableDecl tree) {
      		  // 判断当前符号是否是可追踪的,关于判断标准,在上篇博客有介绍
              boolean track = trackable(tree.sym);
              if (track && tree.sym.owner.kind == MTH) {// 如果当前变量是方法中的并且是可追踪的
                  newVar(tree);
              }
              if (tree.init != null) {
                  scanExpr(tree.init);
                  if (track) {
                      letInit(tree.pos(), tree.sym);
                  }
              }
      }
      

      判断变量是否可追踪的标志为: 给定符号的位置>= startPos && (sym 属于方法 || (当前的符号是 final && 当前符号是classDef的成员))

      由于当前变量是方法中声明的,因此该符号c是可追踪的,因此会执行cAbstractAssignAnalyzer.newVar(JCVariableDecl).如下:

      void newVar(JCVariableDecl varDecl) {
              VarSymbol sym = varDecl.sym;
              vardecls = ArrayUtils.ensureCapacity(vardecls, nextadr);// 对vardecls 进行扩容
              if ((sym.flags() & FINAL) == 0) {// 如果当前符号不是final的
                  sym.flags_field |= EFFECTIVELY_FINAL; // 那么就修改该变量的标识符为有效final 
              }
              sym.adr = nextadr;
              vardecls[nextadr] = varDecl;
              exclVarFromInits(varDecl, nextadr); // 从初始化对应的bitmap中去除
              uninits.incl(nextadr);// 加入到未初始化的bitmap中
              nextadr++;
      }
      

      这个方法,在之前的博客中有介绍,这里不在介绍,方法执行后,符号c的adr为0,符号c成为了有效final,加入到了uninits中, vardecls中保存了符号c(下标为0),nextadr 为1.

    • 第二次执行(int a = 0)如下:

      和int c同样,也会先执行AbstractAssignAnalyzer#newVar方法,此时符号a的adr为1,成为了有效final,加入到了uninits中, vardecls中保存了符号a(下标为1),nextadr 为2.
      不同的是,由于符号有初始化部分(即0),因此会执行AbstractAssignAnalyzer.scanExpr(JCTree)方法,如下:

      void scanExpr(JCTree tree) {
              if (tree != null) {
                  scan(tree);
                  if (inits.isReset()) { // 如果在扫描指定树时, inits 发生了变化,则进行合并
                      merge(tree);
                  }
              }
      }
      

      对于当前,0对应的是JCLiteral,因此会执行TreeScanner#visitLiteral方法,不过此方法是空实现…

      接下来,会执行AssignAnalyzer#letInit方法(AssignAnalyzer#visitVarDef中调用),代码如下:

       void letInit(DiagnosticPosition pos, VarSymbol sym) {
              if (sym.adr >= firstadr && trackable(sym)) {// 如果该变量是可以追踪的
                  if ((sym.flags() & EFFECTIVELY_FINAL) != 0) {// 如果该变量是有效final的
                      if (!uninits.isMember(sym.adr)) {// 如果不是uninits的成员,则修改变量不是有效final的
                          //assignment targeting an effectively final variable
                          //makes the variable lose its status of effectively final
                          //if the variable is _not_ definitively unassigned
                          sym.flags_field &= ~EFFECTIVELY_FINAL;
                      } else {
                      	// 从uninits中去除
                          uninit(sym);
                      }
                  }
                  else if ((sym.flags() & FINAL) != 0) {// 如果该变量是final的
                      if ((sym.flags() & PARAMETER) != 0) {// 是参数
                          if ((sym.flags() & UNION) != 0) { //multi-catch parameter
                              log.error(pos, "multicatch.parameter.may.not.be.assigned", sym);
                          }
                          else {
                              log.error(pos, "final.parameter.may.not.be.assigned",
                                    sym);
                          }
                      } else if (!uninits.isMember(sym.adr)) {
                          log.error(pos, flowKind.errKey, sym);
                      } else {
                          uninit(sym);
                      }
                  }
                  inits.incl(sym.adr);
              } else if ((sym.flags() & FINAL) != 0) {
                  log.error(pos, "var.might.already.be.assigned", sym);
              }
          }
      

      这个方法,同样在之前的博客有介绍,这里不再赘述.

      方法执行后,uninits中去除了符号a,inits中加入了符号a.此时uninits为:10000000000000000000000000000000, inits为: 01000000000000000000000000000000

      uninits 中的1代表的是符号c,inits中的1代表的是符号a

    • 第三次执行(if语句)如下:

      由于这个处理的是JCIf,因此会调用AbstractAssignAnalyzer.visitIf(JCIf),这里首先处理的是if语句中的cond部分(当前就是(c = 1) != c).因此会调用AbstractAssignAnalyzer.scanCond(JCTree)方法,如下:

       void scanCond(JCTree tree) {
         	  ..... 省略无关代码
              } else {
              	/*
              	 * 这里分析的是if(a > 1){ int a }
              	 */
                  scan(tree);
                  if (!inits.isReset())
                      split(tree.type != syms.unknownType);
              }
              if (tree.type != syms.unknownType) {
              	// 重置inits, uninits
                  resetBits(inits, uninits);
              }
          }
      

      这个方法同样在之前的博客中有介绍,这里不在讲解.

      由于((c = 1) != c) 是括号表达式,因此在scanCond方法中执行的是else部分,这里会最终调用TreeScanner.visitParens(JCParens),代码如下:

       public void visitParens(JCParens tree) {
          scan(tree.expr); // 这里处理的是括号表达式中的内容即(c = 1) != c
      }
      

      最终调用的是AbstractAssignAnalyzer.visitBinary(JCBinary),在该方法中,最终会执行如下代码:

      void visitBinary(JCBinary tree){
      	
      	.... 省略其他代码
      	
      	 scanExpr(tree.lhs);
         scanExpr(tree.rhs);
      }
      

      因此这里会最先处理(c = 1),然后再处理 c.

      因此,会首先调用TreeScanner#visitParens方法处理括号表达式–> c = 1,由于是赋值语句,因此最终执行的是AbstractAssignAnalyzer.visitAssign(JCAssign),代码如下:

      public void visitAssign(JCAssign tree) {
              JCTree lhs = TreeInfo.skipParens(tree.lhs);
              if (!(lhs instanceof JCIdent)) {
                  scanExpr(lhs);
              }
              scanExpr(tree.rhs);
              letInit(lhs);
      }
      

      在该方法首先是去除=号左边的括号,这里最终获得的是c,由于c是标识符,因此不会调用scanExpr方,然后处理=号右边的1,由于是字面量,因此会调用TreeScanner#visitLiteral方法,不过此方法是空实现.然后执行AssignAnalyzer#letInit方法,执行之后,inits为11000000000000000000000000000000,uninits为00000000000000000000000000000000.意味着下标为0,1的符号(即 c ,a)都已初始化.

      然后处理的是(c = 1) != c 中 != 号的右值c,由于这里是符号,因此会调用AbstractAssignAnalyzer.visitIdent(JCIdent),代码如下:

      public void visitIdent(JCIdent tree) {
              if (tree.sym.kind == VAR) {
                  checkInit(tree.pos(), (VarSymbol)tree.sym);
                  referenced(tree.sym);
              }
      }
      

      首先调用AbstractAssignAnalyzer.checkInit(DiagnosticPosition, VarSymbol, String)方法,检查变量已经初始化,如下:

      void checkInit(DiagnosticPosition pos, VarSymbol sym) {
              checkInit(pos, sym, "var.might.not.have.been.initialized");
      }
      

      然后调用:

      void checkInit(DiagnosticPosition pos, VarSymbol sym, String errkey) {
              if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
                  trackable(sym) &&
                  !inits.isMember(sym.adr)) {
                  log.error(pos, errkey, sym);
                  inits.incl(sym.adr);
              }
          }
      

      这里很简单,如果符号是可追踪的同时inits中不包含该变量,则输出错误信息,这里的错误信息为 var.might.not.have.been.initialized 所对应的本地化信息,即 可能尚未初始化变量xxx.这也就说明了我们开篇所要讲解的问题

      由于符号c 已经加入到inits中,因此是不会输出错误信息的.

      然后调用AbstractAssignAnalyzer.referenced(Symbol),如下:

      void referenced(Symbol sym) {
              unrefdResources.remove(sym);
      }
      

      这也就意味着符号c 是被引用过的了.

      这里视线回到AbstractAssignAnalyzer#scanCond,处理完((c = 1) != c) 之后,执行如下代码:

      if (!inits.isReset())
              split(tree.type != syms.unknownType);
      

      由于inits的状态不是BitsState.UNKNOWN,因此会执行AbstractAssignAnalyzer.split(boolean),注意,由于((c = 1) != c)的type不是syms.unknownType,因此在split方法中的参数为true.代码如下:

       void split(boolean setToNull) {
               initsWhenFalse.assign(inits);
               uninitsWhenFalse.assign(uninits);
               initsWhenTrue.assign(inits);
               uninitsWhenTrue.assign(uninits);
               if (setToNull) {
                   resetBits(inits, uninits);
               }
           }
      

      执行完之后:

      initsWhenFalse = initsWhenTrue = 11000000000000000000000000000000
      uninitsWhenFalse = uninitsWhenTrue = 00000000000000000000000000000000
      inits = uninits = 00000000000000000000000000000000

      split方法执行完之后,执行如下代码:

      if (tree.type != syms.unknownType) {
              	// 重置inits, uninits
                  resetBits(inits, uninits);
      }
      

      执行完之后, inits = uninits = 00000000000000000000000000000000

      视线回到AbstractAssignAnalyzer#visitIf方法中,处理完if语句conditon部分之后,执行如下代码:

       final Bits initsBeforeElse = new Bits(initsWhenFalse);
       final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
       assignToInits(tree.cond, initsWhenTrue);
       uninits.assign(uninitsWhenTrue);
      

      initsBeforeElse, uninitsBeforeElse 对应的是if语句的else部分的,由于接下来处理if语句中的then,else(如果有的话)部分,那么就需要对这两种情况做初始化.

      准备工作做完之后,处理then部分.如下:

      scan(tree.thenpart);
      

      同样,由于then对应的是JCBlock,因此会调用AbstractAssignAnalyzer.visitBlock(JCBlock),由于当前对应的是空block,因此是不会处理的.

      接下来,回到AbstractAssignAnalyzer#visitIf方法,接下来会执行如下代码:

         if (tree.elsepart != null) {
          final Bits initsAfterThen = new Bits(inits);
          final Bits uninitsAfterThen = new Bits(uninits);
          assignToInits(tree.thenpart, initsBeforeElse);
          uninits.assign(uninitsBeforeElse);
          scan(tree.elsepart);
          andSetInits(tree.elsepart, initsAfterThen);
          uninits.andSet(uninitsAfterThen);
      } else {
      	// 最终inits 的集合是 在if 之前初始化和在condition部分初始化和在then部分初始化的交集
          andSetInits(tree.thenpart, initsBeforeElse);
          uninits.andSet(uninitsBeforeElse);
      }
      

      由于当前,if语句中没有else部分,因此会执行如下代码:

      andSetInits(tree.thenpart, initsBeforeElse);
      uninits.andSet(uninitsBeforeElse);
      

      该步骤处理之后,inits就是与initsBeforeElse交集(与运算)的部分.uninits就是与uninitsBeforeElse交集(与运算)的部分.

      andSetInits方法如下:

      protected void andSetInits(JCTree tree, Bits bits) {
               inits.andSet(bits);
      }
      
      // com.sun.tools.javac.util.Bits.andSet(Bits)
      public Bits andSet(Bits xs) {
           Assert.check(currentState != BitsState.UNKNOWN);
           internalAndSet(xs);
           currentState = BitsState.NORMAL;
           return this;
       }
      
      //  com.sun.tools.javac.util.Bits.internalAndSet(Bits)
      protected void internalAndSet(Bits xs) {
           Assert.check(currentState != BitsState.UNKNOWN);
           sizeTo(xs.bits.length);
           for (int i = 0; i < xs.bits.length; i++) {
               bits[i] = bits[i] & xs.bits[i];
           }
       }
      

      这部分的代码还是很好看的,后面会有一遍文章专门讲解Bits这个类的.

      因此,当我们处理完之后,inits = 11000000000000000000000000000000,uninits = 00000000000000000000000000000000

      看到这里,我们可以总结一下,对于下面的情况:

      public void test(){
      
      		int a = 0;
      		int b ;
      		if(  (b = 1) != 0){
      			int c = 1;
      		}
      		
      	}
      
      

      则最终inits = 11000000000000000000000000000000,uninits = 00000000000000000000000000000000

      而对于下面这种情况:

      	public void test5(){
       	   
          		int a = 0;
          		int b ;
          		if(  (b = 1) != 0){
          			int c = 1;
          		}else{
          			
          			int d = 4;
          		}
          		
          		
       }
      

      则最终inits = 11100000000000000000000000000000,uninits = 00000000000000000000000000000000

      为啥呢,inits也好,uninits也好,都是记录变量初始化的位图,那么对于该方法来说,
      inits,uninits下标为0的就是符号a,下标为1的就是符号b,接下来,由于有then,else部分,
      则在then 中, inits = 11100000000000000000000000000000(下标为2的是符号c),
      而在else部分中,inits = 11100000000000000000000000000000(下标为2的是符号d),
      那么最终进行与运算, inits就是11100000000000000000000000000000,表示在变量表中,下标0,1,2的都是初始化的,而不管是符号c,还是符号d.

    展开全文
  • C++ Json 变量初始化

    千次阅读 2017-03-09 11:26:07
    json中的数组,我一开始理解成跟c++中List,都是用下标去访问,但是这个中间有个问题:比如谁一个json的数字变量a,你要获取第一个数组中的元素给b,不能直接用b=a[0],而是得先定义一个int i=0;b=a[i],不然编译的...

    1、从字符串 解析

    [cpp]  view plain  copy
    1. int ParseJsonFromString()  
    2. {  
    3.   const char* str = "{\"uploadid\": \"UP000000\",\"code\": 100,\"msg\": \"\",\"files\": \"\"}";  
    4.   
    5.   Json::Reader reader;  
    6.   Json::Value root;  
    7.   if (reader.parse(str, root))  // reader将Json字符串解析到root,root将包含Json里所有子元素  
    8.   {  
    9.     std::string upload_id = root["uploadid"].asString();  // 访问节点,upload_id = "UP000000"  
    10.     int code = root["code"].asInt();    // 访问节点,code = 100  
    11.   }  
    12.   return 0;  
    13. }  

    2、从文件中解析

    数据格式:

    {
        "uploadid": "UP000000",
        "code": "0",
        "msg": "",
        "files":
        [
            {
                "code": "0",
                "msg": "",
                "filename": "1D_16-35_1.jpg",
                "filesize": "196690",
                "width": "1024",
                "height": "682",
                "images":
                [
                    {
                        "url": "fmn061/20111118",
                        "type": "large",
                        "width": "720",
                        "height": "479"
                    },
                    {
                        "url": "fmn061/20111118",
                        "type": "main",
                        "width": "200",
                        "height": "133"
                    }
                ]
            }
        ]
    }

    [html]  view plain  copy
      在CODE上查看代码片 派生到我的代码片
    1. int ParseJsonFromFile(const char* filename)  
    2. {  
    3.   // 解析json用Json::Reader  
    4.   Json::Reader reader;  
    5.   // Json::Value是一种很重要的类型,可以代表任意类型。如int, string, object, array...  
    6.   Json::Value root;         
    7.   
    8.   
    9.   std::ifstream is;  
    10.   is.open (filename, std::ios::binary );    
    11.   if (reader.parse(is, root))  
    12.   {  
    13.     std::string code;  
    14.     if (!root["files"].isNull())  // 访问节点,Access an object value by name, create a null member if it does not exist.  
    15.       code = root["uploadid"].asString();  
    16.       
    17.     // 访问节点,Return the member named key if it exist, defaultValue otherwise.  
    18.     code = root.get("uploadid", "null").asString();  
    19.   
    20.   
    21.     // 得到"files"的数组个数  
    22.     int file_size = root["files"].size();  
    23.   
    24.   
    25.     // 遍历数组  
    26.     for(int i = 0; i < file_size; ++i)  
    27.     {  
    28.       Json::Value val_image = root["files"][i]["images"];  
    29.       int image_size = val_image.size();  
    30.       for(int j = 0; j < image_size; ++j)  
    31.       {  
    32.         std::string type = val_image[j]["type"].asString();  
    33.         std::string url = val_image[j]["url"].asString();  
    34.       }  
    35.     }  
    36.   }  
    37.   is.close();  
    38.   return 0;  
    39. }  


    3、在json结构中插入json

    json中的数组,我一开始理解成跟c++中List,都是用下标去访问,但是这个中间有个问题:比如谁一个json的数字变量a,你要获取第一个数组中的元素给b,不能直接用b=a[0],而是得先定义一个int i=0;b=a[i],不然编译的时候会报错,不知道这是为什么,网上也没找到对应说明,如果哪位大神看到的话留言解答一下吧,谢谢~

    [html]  view plain  copy
      在CODE上查看代码片 派生到我的代码片
    1. Json::Value arrayObj;   // 构建对象  
    2. Json::Value new_item, new_item1;  
    3. new_item["date"] = "2011-12-28";  
    4. new_item1["time"] = "22:30:36";  
    5. arrayObj.append(new_item);  // 插入数组成员  
    6. arrayObj.append(new_item1); // 插入数组成员  
    7. int file_size = root["files"].size();  
    8. for(int i = 0; i < file_size; ++i)  
    9.   root["files"][i]["exifs"] = arrayObj;   // 插入原json中  

    4、输出 

    [html]  view plain  copy
      在CODE上查看代码片 派生到我的代码片
    1. // 转换为字符串(带格式)  
    2. std::string out = root.toStyledString();  
    3. // 输出无格式json字符串  
    4. Json::FastWriter writer;  
    5. std::string out2 = writer.write(root);  

    5、解析数组 

    [html]  view plain  copy
      在CODE上查看代码片 派生到我的代码片
    1. std::string strValue="{\"key1\":\"value1\",\"array\":[{\"key2\":\"value2\"},{\"key2\":\"value3\"},{\"key2\":\"value4\"}]}";     
    2.     Json::Reader reader;//json解析    
    3.     Json::Value value;//表示一个json格式的对象    
    4.     if(reader.parse(strValue,value))//解析出json放到json中区    
    5.     {    
    6.         std::string out=value["key1"].asString();    
    7.         std::cout<<out<<std::endl;    
    8.         const Json::Value arrayObj=value["array"];//迭代器    
    9.         for (int i=0; i < arrayObj.size();i++)      
    10.         {    
    11.             out=arrayObj[i]["key2"].asString();    
    12.             std::cout<<out;    
    13.             if(i!=arrayObj.size()-1)    
    14.                 std::cout<<std::endl;;    
    15.         }    
    16.     }    
    不含迭代器的方法:

    [html]  view plain  copy
      在CODE上查看代码片 派生到我的代码片
    1. string str = "[{"money":"100"},{"SB":"200"}]";  
    2.     Json::Reader reader;//json解析    
    3.     Json::Value value;//表示一个json格式的对象    
    4.         
    5.     if(reader.parse(str,value))//解析出json放到json中区    
    6.     {    
    7.         for(int i = 0;i<value.size();i++)  
    8.     {  
    9.         out=arrayObj[i]["SB"].asString();   
    10.     }  
    11.     }    
    展开全文
  • 莫偷懒!成员变量一定要初始化!

    千次阅读 2014-04-22 14:35:01
    成员变量一定要初始化! 未初始化的bool成员变量在Debug下默认值为false,Test下默认true。一个bug查了一晚上,原因就是这个. 人物创建的场景在Debug下正常,在Test和Release下不正常,就是镜头不对。然后就盯着...
  •  1)局部变量分配在栈中,如果不初始化,则为不可预料值。编译时,编译器将抛出一个编号为C4700警告错误(local variable '变量名' used without having been initialized)。  表4.1代码测试了局部变量未...
  • 原文:http://www.cppblog.com/lai3d/archive/2009/07/08/89514.html未初始化的bool成员变量在Debug下默认值为false,Test下默认true。一个bug查了一晚上,原因就是这个.人物创建的场景在Debug下正常,在Test和...
  • 三类初始化值说明 对于0xcccccccc和0xcdcdcdcd,在 Debug 模式下,VC 会把未初始化的栈内存上的指针全部填成 0xcccccccc ,当字符串看就是 “烫烫烫烫……”;会把未初始化的堆内存上的指针全部填成 0xcdcdcdcd,当...
  • Debug 与 Release 版本 变量 初始化(zz)

    千次阅读 2013-07-16 19:12:35
    //z 2013-07-16 19:09:30 IS2120@BG57IV3.T40682136 .K[T83,L895,R28,V1279] Surviving the Release Version By Joseph M. Newcomer, 16 Jul 2001 Introduction ...OK, your program works....
  • 数组的动态初始化和静态初始化

    千次阅读 2019-01-15 17:33:44
    1. 数组:同一种数据类型的集合,存储数据 声明的数据类型和分配空间的数据类型必须一样的;长度只能用数字; 元素:数组里边的每一个值 ...2. 动态初始化: 声明:数据类型[] 变量名; 数据类型 变量名[]; ...
  • 数组初始化:指定初始化项目

    千次阅读 2017-08-08 19:15:00
    翻阅笔记系列 看以前的笔记,感觉就是 我以前还了解过这... //C99规定,初始化列表中使用带有方括号的元素下标可以指定某个特定元素。指定部分为0.//如果多次对一个元素进行初始化,则最后一次有效。如: int arr[6] =
  • C++数组初始化

    千次阅读 2018-12-28 11:19:39
    C++数组初始化 定义: int *pia = new int[10]; // array of 10 uninitialized ints 此 new 表达式分配了一个含有 10 个 int 型元素的数组,并返回指向该数组第一个元素的指针,此返回值初始化了指针 pia...
  • 找出数组中其中最大的数及其下标

    千次阅读 2019-03-25 13:59:56
    1、在一个有8个整数(18,25,7,36,13,2,89,63)的数组中找出其中最大的数及其下标。 代码如下: public static void main(String[] args) { ... //局部变量需要初始化 int max = a[0];//假设a[0]是最大的值,...
  • 指针的初始化和赋值

    千次阅读 多人点赞 2018-01-05 17:00:58
    指针初始化时,“=”的右操作数必须为内存中数据的地址,不可以是变量,也不可以直接用整型地址值(但是int*p=0;除外,该语句表示指针为空)。此时,*p只是表示定义的是个指针变量,并没有间接取值的意思。 例如: ...
  • 53_数组_数组的三种初始化方式

    千次阅读 2017-06-09 15:27:25
    组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也按照实例变量同样的方式隐式初始化。 静态初始化 除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素...
  • c语言初始化数组为特定值

    千次阅读 2013-07-01 22:04:35
    1. 不初始化 int a[5][5]; 当数组是全局变量时,默认初始化为0 当数组是全局变量时,默认是堆栈中的随机值 2.初始化一个值 int a[5][5]={0}; 无论是全局变量,还是局部变量都初始化为0 int a[5][5]={1}; 无论是...
  • C语言结构体指针初始化

    千次阅读 2016-12-08 16:49:54
    经过上网搜索之后知道的错误的原因,没有初始化。。。 需要指出的是:结构体指针在使用之前是需要初始化的! 同时需要注意的是:  (2)结构体变量中的元素访问方式:只有一种,用"."或者"->"的方式来...
  • 动态内存空间初始化方式的比较

    千次阅读 2013-06-18 17:10:41
    1. 通过下标的方式进行初始化; 2. 通过移动指针来进行初始化; /****************** 下标方式 ******************/ #include #include int main(void) { int *p = (int *)malloc(5 * sizeof(int)); printf...
  • Java中数组的定义及初始化

    千次阅读 2018-10-23 00:14:18
    1动态初始化(声明并开辟数组)1.2 静态初始化2. 引用传递3. 二维数组的基本了解4. 数组与方法的互操作4.1 方法接收数组4.2 方法返回数组4.3 方法修改数组5. Java对数组的支持5.1 排序5.2 数组的拷贝5.2.1 数组的...
  • C语言中指针初始化和常规运算

    千次阅读 2017-05-30 22:00:11
    指针初始化和指针间的常规运算。
  • 二维数组的初始化

    千次阅读 2020-07-08 17:19:06
    1.不分行的初始化,所有数组元素放在一对 ...(3)省略一维长度下标时的初始化(行下标) a[][3] = {1, 2, 3, 4, 5, 6}; // 数组元素个数除以列数,正好除尽,所得的商就是行数。 a[][3] = {1, 2, 3, 4}; // 数组元素
  • C++(6):vector的初始化方式

    千次阅读 2018-08-15 18:01:26
    C++中的vector有多种初始化方式,每种方式都有自己的特点和优势,熟练掌握和运用不同的方式能够在实际编程过程中大大提高自己的编程效率。 下面逐一介绍各种不同方法。(以下代码默认包含头文件&lt;vector&...
  • 重点是用数组初始化vector数组啊

    千次阅读 2016-10-10 21:13:43
    自己在随便初始化vector变量时候不知道 vector a(m, m + 9);里面9所选的大小应该为数组的大小,选成了8.导致一直有问题。。啊啊啊,细节决定成败啊。还有一点就是方法中要注意while (j >= 0 && i >= 0)中i>=0的判断...
  • vector 初始化 与遍历

    千次阅读 2015-03-17 11:07:35
    ;">#include #include #include #include using namespace std;... //vector<T> v(n)形式,v包含n 个值初始化的元素 ...如果没有给出元素的初始化式,那么标准库将提供一个值初始化的(valueinitialized)...
  • 数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。Java 语言中提供的数组是...这次我们来看Java 数组的声明、创建和初始化。 首先必须声明数组变量,才能在程序中...
  • 数组的基本概念和初始化方式

    千次阅读 2016-05-01 15:52:05
    · 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。 · 数组的元素可以是任何数据类型,包括基本类型和引用类型。 · C和C++中的数组都可以分配在栈上面,而JAVA中的数组是...
  • C语言一维数组的定义及初始化

    千次阅读 2020-10-14 13:39:11
    一维数组初始化 1. 定义一维数组 一般形式: 类型说明符 数组名[常量表达式] eg: int a[10]; 下标从0开始,这10个元素是从a[0]~a[9] 类型说明符:指定数组中元素的类型 ,a中所有元素都是int类型的 数组名命名...
  • 系统学习深度学习(十四)--权重初始化Xavier

    万次阅读 多人点赞 2017-02-27 09:55:20
    “Xavier”初始化方法是一种很有效的神经网络初始化方法,方法来源于2010年的一篇论文《Understanding the difficulty of training deep feedforward neural networks》,可惜直到近两年,这个方法才逐渐得到更多人...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 138,760
精华内容 55,504
关键字:

其中被初始化的下标变量是