精华内容
下载资源
问答
  • 利用构造函数对类对象进行初始化

    千次阅读 2017-05-01 20:25:22
    有人试图在声明数据成员初始化。如 class Time { hour=0; minute=0; sec=0; }; 这是错误的。因为并不是一个实体,而是一种抽象类型,并不占存储空间,显然无处容纳数据。 如果一个中所有成员都是...
    9.1利用构造函数对类对象进行初始化
    9.1.1对象的初始化
    在程序中常常需要对变量赋初值,即对其初始化。
    那么,怎样使他们得到初值呢?有人试图在声明类时对数据成员初始化。如
    class Time
    {
    hour=0;
    minute=0;
    sec=0;
    };
    这是错误的。因为类并不是一个实体,而是一种抽象类型,并不占存储空间,显然无处容纳数据。
    如果一个类中所有成员都是公用的,则可以在定义对象时对数据成员进行初始化。如:
    class Time
    {
    pubic:
    hour;
    minute;
    sec;
    };
    Time t1={14,56,30};
    这种情况和结构体变量的初始化是类似的,在一个花括号内顺序列出各公用数据成员的值,两个值之间用逗号分隔。但是,如果数据成员


    是私有的,或者类中有private或protected的数据成员,就不能用这种方法初始化。
    9.1.2用构造函数实现数据成员的初始化
    C++提供了构造函数(constructor)来处理对象的初始化。构造函数时一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它


    ,而是在建立对象时自动执行。构造函数是在声明类的时候由类的设计者定义的,程序用户只须在定义对象的同时指定数据成员的初值即


    可。
    构造函数的名字必须与类名同名,而不能任意命名,以便编译系统能识别它并把它作为构造函数处理。它不具有任何类型,不返回任何值



    例9.1 在例8.3的基础上,用构造函数为对象的数据成员赋初值。




    #include<iostream>
    using namespace std;
    class Time
    {
    public:
    Time()  //定义构造成员函数,函数名和类名相同
    {
    hour=0;  //利用构造函数对对象中的数据成员赋初值
    minute=0;
    sec=0;
    }
    void set_time();  //公用成员函数
    void show_time();
    private:             //数据成员为私有的
    int hour;
    int minute;
    int sec;
    };
    void Time::set_time()  //在类外定义set_time函数
    {
    cin>>hour;
    cin>>minute;
    cin>>sec;
    }
    void Time::show_time()
    {
    cout<<hour<<":"<<minute<<":"<<sec<<endl;
    }
    int main()
    {
    Time t1;              //建立对象t1,同时调用构造函数t1.Time()
    t1.set_time();    //对t1的数据成员赋值
    t1.show_time();//显示t1的数据成员的值
    Time t2;     //建立对象t2,同时调用构造函数t2.Time()
    t2.set_time();        
    t2.show_time();
    return 0;
    }




    /*在类中定义了构造函数Time,它和所在的类同名。在建立对象时自动执行构造函数,根据构造函数Time的定义,其作用是对该对象中的数


    据成员赋予初值0。
    请不要误认为是在声明类时直接对程序数据成员赋初值(那是不允许的),赋值语句是写在构造函数的函数体中的,只有在调用构造函数


    时才执行这些赋值语句,对当前对象中的数据成员赋值。
        程序在运行时首先建立对象t1,在执行构造函数过程中对t1中的数据成员赋予初值0。然后再执行主函数中的t1.set_time函数,从键盘输入


    新值赋给对象t1的数据成员,再输出t1的数据成员的值。
       上面是在类内定义构造函数的,也可以只在类内对构造函数进行声明而在类外定义构造函数。将程序中第5~9行改为下面一行:
    Time();//对构造函数进行声明
    在类外定义构造函数:
    Time::Time()                   //在类外定义构造函数,要加上类名Time和域限定符"::"。
    {
    hour=0;
    minute=0;
    sec=0;
    }
    有关构造函数的使用,有以下说明:
    1.什么时候调用构造函数呢?在建立类对象时会自动调用构造函数。在建立对象时系统为该对象分配内存单元,此时执行构造函数,就把指


    定的初值送到有关
    数据成员的存储单元中。每建立一个对象,就调用一次构造函数。在上面的程序中,在主函数中定义了一个对象t1,在此时,就会自动调


    用t1对象中的构造函数Time,使各数据成员的值为0。
    2.构造函数没有返回值,因此也没有类型,它的作用只是对对象进行初始化。因此也不需要在定义构造函数时声明类型,这是它和一般函


    数的一个重要的不同之点。不能写成;
    int Time()
    {...}

    void ?time()
    {...}
    3.构造函数不需要用户调用,也不能被用户调用。下面用法是错误的:
    t1.Time();    //试图用调用一般成员函数的方法调用构造函数
    构造函数是在定义对象时由系统自动执行的,而且只能执行一次。构造函数一般声明为public。
    4.可以用一个类对象初始化另一个类对象,如
    Time t1;         //建立对象t1,同时调用构造函数t1.Time()
    Time t2=t1;         //建立对象t2,并用一个t1初始化t2
    此时,把对象t1的各数据成员的值复制到t2相应各成员,而不调用构造函数t2.Time()。
    5.在构造函数的函数体中不仅可以对数据成员赋初值,而且可以包含其他语句,例如cout语句。但是一般不提倡在构造函数中加入与初始化


    无关的内容,以保持程序的清晰。
    6.如果用户自己没有定义构造函数,则C++系统会自动生成一个构造函数,只是这个构造函数的函数体是空的,也没有参数,不执行初始化


    操作。
    9.1.3 带参数的构造函数
    有时用户希望对不同的对象赋予不同的初值,怎么办?
    可以采用带参数的构造函数,在调用不同对象的构造函数时,从外面将不同的数据传递给构造函数,以实现不同的初始化。构造函数首部


    的一般形式为:
    构造函数名(类型1 形参1,类型2 形参2,...)
    前面已说明:用户是不能调用构造函数的,因此无法采用常规的调用函数的方法给出实参(如fun(a,b);)。实参是在定义对象时给出的。定


    义对象的一般形式为
    类名 对象名(实参1,实参2,...);
    在建立对象时把实参的值传递给构造函数相应的形参,把他们作为数据成员的初值。
    例9.2 有两个长方柱,其高、宽、长分别为(1)12,20,25;(2)10,14,20。求他们的体积。编写一个基于对象的程序,在类中用带参数的


    构造函数对数据成员初始化。
    #include<iostream>
    using namespace std;
    class Box  //声明Box类
    {
    public:
    Box(int,int,int);//声明带参数的构造函数
    int volume();//声明计算体积的函数
    private:
    int height;
    int width;
    int length;
    };
    Box::Box(int h,int w,int len) //在类外定义带参数的构造函数
    {
    height=h;
    width=w;
    length=len;
    }
    int Box::volume()  //定义计算体积的函数
    {
    return(height*width*length);
    }
    int main()
    {
    Box box1(12,25,30);  //建立对象box1,并指定box1的高、宽、长的值
    cout<<"The volume of box is"<<box1.volume()<<endl;
    Box box2(15,30,21);
    cout<<"The volume of box2 is"<<box2.volume()<<endl;
    return 0;
    }
    构造函数Box有3个参数(h,w,l),分别代表高、宽、长。在主函数中定义对象box1时,同时给出函数的实参12,25,30。然后在cout语句中调用


    函数box1.volume(),并输出box1的体积。对box2也类似。
    注意:定义对象的语句形式是
    Box box1(12,25,30);
    可以知道;
    1.带参数的构造函数中的形参,其对应的实参是在建立对象时给定的。即在建立对象时同时指定数据成员的初值。
    2.定义不同对象时用的实参是不同的,他们反映不同对象的属性。用这种方法可以方便地实现对不同对象进行不同的初始化。


    9.1.4  用参数初始化表对数据成员初始化
           在9.1.3节中介绍的是在构造函数的函数体内通过赋值语句对数据成员实现初始化。C++还提供另一种初始化数据成员的方法——参数初试化表来实现对数据成员的初始化。这种方法不在函数体内对数据成员初始化,而是在函数首部实现。如例9.2中定义构造函数可以改用以下形式:
    Box::Box(int h,int w,int len):height(h),width(w),length(len){}
    即在原来函数首部的末尾加一个冒号,然后列出参数的初始化表。上面的初始化表表示:用形参h的值初始化数据成员height,用形参w的


    值初始化数据成员width,用形参len的值初始化数据成员length。后面的花括号是空的,即函数体是空的,没有任何执行语句。这种形式的


    构造函数的作用和例9.2中在类外定义的Box构造函数相同。用参数的初始化表法可以减少函数体的长度,使结构函数显得精炼简单。这样


    就可以直接在类体中(而不是在类外)定义构造函数。尤其当需要初始化的数据成员较多时更显其优越性。许多C++程序人员喜欢用这种


    方法初始化所有数据成员。
    带有参数初始化表的构造函数的一般形式如下:
    类名::构造函数名([参数表])[:成员初始化表]
    {
    [构造函数体]
    }
    其中方括号内为可选项(可有可无)。
    说明:如果数据成员是数组,则应当在构造函数的函数体中用语句对其赋值,而不能在参数初始化表中对其初始化。如:
    class Student
    {
    public:
    Student(int n,char  s,nam[]):num(n),sex(s)  //定义构造函数
    {
    strcpy(name,mam);
    }
    private:  //函数体
    int num;
    char sex;
    char name[20];
    };
    可以这样定义对象stud1:
    Student stud1(10101,'m',"Wang_li");
    利用初始化表,把形参n得到的值10101赋给私有数据成员num,把形参s得到的值'm'赋给sex,把形参数组nam的各元素的值通过strcpy函数


    复制到name数组中。这样对象stud1中所有的数据成员都初始化了,此对象是有确定内容的。
    9.1.5 构造函数的重载
         在一个类中可以定义多个构造函数,以便为对象提供不同的初始化方法,供用户选用。这些构造函数具有相同的名字,而参数的个数或参数的类型不相同。这称为构造函数的重载。在第4.6节中所介绍的函数重载的知识也适用于构造函数。
    通过下面的例子可以了解怎样应用构造函数的重载。
    例9.3  在例9.2的基础上,定义两个构造函数,其中一个无参数,一个有参数。
    #include<iostream>
    using namespace std;
    class Box
    {
    public:
    Box();  //声明一个无参构造函数Box
    Box(int h,int w,int len):height(h),width(w),length(len){}//定义一个有参的构造函数,用参数的初始化表对数据成员初始化
    int volume();  //声明成员函数volume
    private:
    int height;
    int width;
    int length;
    };
    Box::Box()  //在类外定义无参构造函数Box
    {
    height=10;
    width=10;
    length=10;
    }
    int Box::volume() //在类外定义声明成员函数volume
    {
    return(height*width*length);
    }
    int main()
    {
    Box box1;//建立对象box1,不指定实参
    cout<<"The volume of box1 is"<<box1.volume()<<endl;
    Box box2(15,30,25);
    cout<<"The volume of box2 is"<<box2.volume()<<endl;
    return 0;
    }


          在类中声明了一个无参数构造函数Box(),在类外定义的函数体中对私有数据成员赋值。第2个构造函数是直接在类体中定义的,用参数初始化表对数据成员初始化,函数有3个参数,需要3个实参与之对应。这两个构造函数同名(都是Box),那么系统怎么辨别调用的是哪一个构造函数呢?编译系统是根据函数调用的形式去确定对应哪一个构造函数。
          在主函数中,建立对象box1时没有给出参数,系统找到与之对应的无参构造函数Box,执行此构造函数的结果是使3个数据成员的值均为10。然后输出box1的体积。建立对象box2时给出3个实参,系统找到有3个形参的构造函数Box与之对应,执行此构造函数的结果是使3个数据成员的值为15,30,25。然后输出box2的体积。
          在本程序中定义了两个同名的构造函数,其实还可以定义更多的重载构造函数。例如还可以有以下的构造函数原型:
    Box::Box(int h);             //有一个参数的构造函数
    Box::Box(int h,int w);       //有两个参数的构造函数
    在建立对象时可以给出一个参数和两个参数,系统会分别调用相应的构造函数。
    说明:
    1.  在建立对象时不必给出实参的构造函数,称为默认构造函数(default constructor)。显然,无参构造函数属于默认构造函数。一个类只能有一个默认构造函数。如果用户未定义构造函数,则系统会自动提供一个默认构造函数,但它的函数体是空的,不起初始化作用。如果用户希a员有初值,就必须自己定义构造函数。
    2.如果在建立对象时选用的是无参构造函数,应注意正确书写定义对象的语句。如本程序中有以下定义对象的语句:
    Box box1;//建立对象的正确形式
    注意不要写成Box box1();//建立对象的错误形式,不应该有括号
    上面的语句并不是定义Box类的对象box1,而是声明一个普通函数box1,此函数的返回值为Box类型。在程序中不应出现调用无参构造函数


    (如Box()),请记住:构造函数时不能被用户显式调用的。
    3.尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时只执行其中一个构造函数,并非每个构造函数都被执行



    9.1.6 使用默认参数的构造函数
    构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使形参取默认值。


    /*例9.4 将例9.3程序中的构造函数改用含默认值的参数,宽、高、长的默认值均为10。
    */
    #include<iostream>
    using namespace std;
    class Box
    {
    public:
    Box(int h=10,int w=10,int len=10);  //在声明构造函数时指定默认参数,形参名可以省略,即写成Box(int =10;int =10;int =10);
    int volume();  //声明成员函数volume
    private:
    int height;
    int width;
    int length;
    };
    Box::Box(int h,int w,int len)//在定义函数时可以不指定默认参数
    {
    height=h;
    width=w;
    length=len;
    }
    /*对构造函数的定义(第12~16行)也可以改写成参数初始化表的形式:
    Box::Box(int h,int w,int len):height(h),width(w),length(len){}
    */
    int Box::volume() //在类外定义声明成员函数volume
    {
    return(height*width*length);
    }
    int main()
    {
    Box box1;//建立对象box1,不指定实参
    /* cout<<"The volume of box1 is"<<box1.volume()<<endl;
    Box box2(15,30,25);
    cout<<"The volume of box2 is"<<box2.volume()<<endl;*/
    cout<<"The volume of box1 is"<<box1.volume()<<endl;
    Box box2(15);
    cout<<"The volume of box2 is"<<box2.volume()<<endl;
    Box box3(15,30);
    cout<<"The volume of box3 is"<<box3.volume()<<endl;
    Box box4(15,30,20);
    cout<<"The volume of box4 is"<<box4.volume()<<endl;
    return 0;
    }
    /*由于在定义对象box1时没有给实参,系统就调用默认构造函数,各形参的值均取默认值10,即
    box1.height=10;box1.width=10;box1.length=10
    在定义对象box2时只给定一个实参15,它传给形参h(长方柱的高),形参w和len未得到实参过来的值,就取默认值10。即
    box2.height=15;box2.width=10;box2.length=10;
    说明:
    1.应该在什么地方指定构造函数的默认参数?应在声明构造函数时指定默认值,而不能只在定义构造函数时指定默认值。因为类声明是放在


    头文件中的,它是
    类的对外接口,用户是可以看到的,而函数的定义是类的实现细节,用户往往看不到的。在声明构造函数时指定默认参数值,使用户知道


    在建立对象时怎样使用
    默认参数。
    2.程序第5行在声明构造函数时,形参名可以省略.
    3.如果构造函数的全部参数都指定了默认值,则在定义对象时可以给一个或几个实参,也可以不给出实参。由于不需要实参也可以调用构


    造函数,因此全部参数
    都指定了默认值的构造函数也属于默认构造函数。前面曽提到过:一个类只能有一个默认构造函数,也就是说,可以不用参数而调用的构


    造函数,一个类只能有一个。其道理是显然的,是为了避免调用时的歧义性。如果同时定义了下面两个构造函数,是错误的。
    Box();//声明一个无参构造函数
    Box(int =10,int =10,int=10);声明一个全部参数都指定了默认值的构造函数
    在建立对象时,如果写成
    Box box1;
    编译系统无法识别应该调用哪个构造函数,出现歧义性,编译时报错。应该避免这种情况。
    4.在一个类中定义了全部是默认参数的构造函数后,不能再定义重载的构造函数。例如在一个类中有以下构造函数声明:
    Box(int=10,int=10,int=10);//指定全部为默认参数
    Box();//声明无参的构造函数
    Box(int,int);//声明有两个参数的构造函数
    若有以下定义语句:
    Box box1;//是调用上面第1个构造函数,还是调用第二个构造函数
    Box box2(15,30)//是调用上面第1个构造函数,还是调用第3个构造函数
    应该执行哪一个构造函数呢?出现歧义性。但如果构造函数中的参数并非全部为默认值时,就要分析具体情况。如有以下3个原型声明:
    Box();//无参构造函数
    Box(int,int=10,int=10);//有1个参数不是默认参数
    Box(int,int);//有两个参数的构造函数
    若有以下定义对象的语句:
    Box box1;//正确,不出现歧义性,调用第1个构造函数
    Box box2(15);//调用第2个构造函数
    Box box3(15,30);//错误,出现歧义性
    很容易出错,要十分仔细。因此,一般不应同时使用构造函数的重载和有默认参数的构造函数。






































     

















    展开全文
  • 常量,引用,对象成员的初始化要在成员初始化表中进行,不可以直接=号赋值。 class B{ } class A{  public:  A():number(n),ref(r),b(b1){...}  private:  const int number;  int & ref;  B b...
    常量,引用,对象成员的初始化要在成员初始化表中进行,不可以直接=号赋值。

    class B{
    }

    class A{
         public:
                 A():number(n),ref(r),b(b1){...}
         private:
           const int number;
           int & ref;
           B b;
        
    }
    那为什么是常量,引用,对象成员这三者不可以直接初始化,而要放在成员初始化表中呢?

    1.对于常量:

       常量是不可以被赋值的,不能在创建对象的时候点取常量成员然后赋值给它.
       A  a;
       a.number=10;  // error;
       更加不可以直接就在类的定义中赋值
       class A{
         const int number=10;
       } 
       因为每个对象的number可能是不一样的,这样直接赋值的话,就是要求类的所有对象的number都是固定的,这样很明显不合逻辑。
       另外,有些书认为这样是在给常量赋值,所以是不允许的。

    2.对于引用:

       引用本质上来讲也是一种常量,那么它也是不可重新指派的,初始化之后就固定不变。

    3.对于对象成员:

       对象成员出现在成员初始化表上,实际上相当于调用该对象所属类的构造函数,b(b1)即是以b1为参数调用B的构造函数。

       如果不这样做的话,要实现初始化似乎只能传递对象参数生成临时对象来实现,这样的话,根本没有起到初始化的作用。
       请看例子:
    #include <iostream>

    using namespace std;

    class StudentID{

        public:
            StudentID(int id){
               value =id
               cout<<"Assigning student id "<<value<<endl;
            }
            ~StudentID(){
               cout<<"Destructing id " << value <<endl;
            }

        protected:
            int value;
    }

    class Student{
        public:
            Student(char * pName="noName",int ssID=0){
                cout<<"Constructing student "<<pName<<endl;
                strcpy(name,pName);
                name[sizeof(name)-1]='\0';
                StudentID id(ssID);
            }

        protected:
            char name[20];
            StudentID id;
    }

    int main(){
        Student s("Randy",9818);
    }
    print:
    Assigning student id 0
    Constructing student Randy         
    Assigning student id 9818  // 当传递9818给StudentID id(ssID)的时候,调用StudentID的构造函数,这样就在Student的构造函数产生了一个局部对象
    Destructing id 9818           // 当
    Student的构造函数对象结束的时候,该对象就析构了,那么id 9818就没有了,实际上9818没有初始化Randy的id;
    Destructing id 0                // Randy的id仍然为0,程序结束的时候析构。


    构造对象的顺序--按照成员属性声明的顺序,而不是成员初始化表的顺序。

    静态对象之构造一次。

    所有全局对象在main()函数之前被构造,且一般按照声明的顺序。
    展开全文
  • 对象初始化

    千次阅读 2018-06-03 17:26:29
    本文主要通过例子来理解 Java 中对象初始化顺序问题

    转载请注明原创出处,谢谢!

    HappyFeet的博客

    本文主要通过例子来理解 Java 中类和对象的初始化。


    1、首先来看一个最简单的例子(无父类且无静态成员变量)
    public class OrderOfInitialization1 {
    
        public static void main(String[] args) {
            House house = new House();
            house.f();
        }
    
    }
    
    class Window {
        Window(int market) {
            System.out.println("Window(" + market + ")");
        }
    }
    
    class House {
        Window w1 = new Window(1);// before constructor
    
        House() {
            // show we're in constructor
            System.out.println("House()");
            w3 = new Window(33);//reinitialize w3
        }
    
        Window w2 = new Window(2);// after constructor
    
        void f() {
            System.out.println("f()");
        }
    
        Window w3 = new Window(3);//at end
    }
    
    output:
        Window(1)
        Window(2)
        Window(3)
        House()
        Window(33)
        f()
    

    从输出结果分析,House 实例变量的初始化顺序是: w1w2w3 ,然后才是构造函数。

    即:实例变量按照其在代码中出现的顺序执行初始化,然后执行构造函数里面的初始化。

    2、接下来看一个稍微复杂一些的例子(有父类但无静态成员变量)
    public class OrderOfInitialization2 {
    
        public static void main(String[] args) {
            SubClass subClass = new SubClass();
        }
    
    }
    
    class Print {
        Print(int i) {
            System.out.println("new Print(" + i + ")");
        }
    }
    
    class SuperClass {
        Print print1 = new Print(1);
    
        public SuperClass() {
            System.out.println("new SuperClass()");
        }
    
        Print print2 = new Print(2);
    }
    
    class SubClass extends SuperClass {
        Print print3 = new Print(3);
    
        public SubClass() {
            //这个地方其实是调用了父类的默认的无参构造函数,super();
            //如果父类没有无参构造函数,则这个地方必须显式的调用父类的构造函数,否则编译不通过
            System.out.println("new SubClass()");
        }
    
        Print print4 = new Print(4);
    }
    
    output:
        new Print(1)
        new Print(2)
        new SuperClass()
        new Print(3)
        new Print(4)
        new SubClass()
    

    从输出结果分析:这个地方是先调用了父类 SuperClass 的构造函数,然后调用子类 SubClass 的构造函数。

    即:如果一个类有父类,在实例化子类的时候,会先执行父类的构造函数,然后执行子类的构造函数。

    3、继续看一个更复杂一些的例子(有父类且有静态成员变量)
    public class OrderOfInitialization3 {
    
        public static void main(String[] args) {
            Man man = new Man();
            Man man1 = new Man();
        }
    
        static Print1 print0 = new Print1(0);
    }
    
    class Print1 {
        Print1(int i) {
            System.out.println("new Print1(" + i + ")");
        }
    }
    
    class People {
        Print1 print1 = new Print1(1);
    
        public People() {
            System.out.println("new People()");
        }
    
        Print1 print2 = new Print1(2);
    
        static Print1 print5 = new Print1(5);
    }
    
    class Man extends People {
    
        Print1 print3 = new Print1(3);
    
        public Man() {
            System.out.println("new Man()");
        }
    
        Print1 print4 = new Print1(4);
    
        static Print1 print6 = new Print1(6);
    }
    
    output:
           new Print(0)
           new Print(5)
           new Print(6)
           new Print(1)
           new Print(2)
           new People()
           new Print(3)
           new Print(4)
           new Man()
           new Print(1)
           new Print(2)
           new People()
           new Print(3)
           new Print(4)
           new Man()
    

    从输出结果分析:这里首先执行了 OrderOfInitialization3 类的静态变量 print0 的初始化(输出 new Print(0)),然后执行静态方法 main;紧接着是执行 People 类的静态成员变量 print5 的初始化(输出 new Print(5)),再接着是 Man 类的静态成员变量 print6 的初始化(输出 new Print(6));之后是 People 的实例变量(输出new Print(1)、new Print(2))、构造函数(输出 new People())初始化,最后才是 Man 实例变量(输出 new Print(3)、new Print(4))、构造函数(输出 new Man())的初始化。在第二次实例化一个 Man 的时候,所有的静态成员变量都没有相应的输出,即静态成员变量只初始化了一次。

    所以这个地方执行的顺序是:首先执行 main 所在类的静态成员变量的初始化,然后是 Man 的父类的静态成员变量的初始化,然后是子类的静态成员的初始化;接着是父类的构造函数,最后才是子类的构造函数。

    这个地方 Man 实例化了两次,但是其父类和本身的静态成员变量只初始化了一次。

    为什么静态成员变量只会初始化一次呢?

    • 实际上,静态成员变量初始化的过程本质上就是一个类的加载和初始化的过程,虚拟机保证了在同一个类加载器下,一个类型只会初始化一次。
    4、总结一下

    这个地方把类和对象分开会更好理解一点。

    (1)类的初始化

    • 静态成员变量初始化发生在静态方法之前
    • 父类的初始化必须在子类初始化之前
    • 静态成员变量的初始化顺序为其在代码中出现的顺序

    (2)实例化对象

    • 如果有父类,先执行父类的实例化
    • 成员变量初始化发生在构造函数之前
    • 成员变量的初始化顺序为其在代码中出现的顺序

    (3)实例化对象之前如果该类没有初始化,必须先执行该类的初始化。

    5、最后看一个比较特殊的例子
    public class NestedInitialization {
    
        public static void main(String[] args) {
            staticFunction();
        }
    
        static NestedInitialization st = new NestedInitialization();
    
        static {
            System.out.println("1");
        }
    
        {
            System.out.println("2");
        }
    
        NestedInitialization() {
            System.out.println("3");
            System.out.println("a=" + a + ",b=" + b);
        }
    
        public static void staticFunction() {
            System.out.println("4");
        }
    
        int a = 110;
        static int b = 112;
    
    }
    
    output:
    	2
    	3
    	a=110,b=0
    	1
    	4
    

    这个例子的特殊性在于,**该类还没有完成初始化,就去实例化一个该类的对象。**我们从类的生命周期来分析,一个类会经历加载、验证、准备、解析、初始化、使用和卸载七个阶段,在执行到 static NestedInitialization st = new NestedInitialization(); 这一步时,已经是类的初始化阶段了,此时,NestedInitialization 类里面的静态成员的值都还是准备阶段设置的初始零值,即 static NestedInitialization st = null , static int b = 0; ,然后这个地方需要实例化 NestedInitialization,所以是以此时的状态去实例化 NestedInitialization 类的,执行类的实例化,然后在继续类的初始化。所以才会出现上面的输出结果。

    参考资料:

    (1)《深入理解 Java 虚拟机》周志明 著.

    (2)《Thinking in Java(4th Edition)》

    展开全文
  • Java类成员初始化

    千次阅读 2013-05-30 15:03:16
    下面就Java类成员初始化方式做一个梳理,希望大家有益。 一、 所有变量使用前都要得到恰当的初始化 Java尽力保证:所有变量在使用前都得到恰当的初始化。 对于成员变量,Java按照自动初始化的方式,如果是...

    Java类成员初始化与C++初始化的方式有很多不同,这部分知识相对比较基础,从而导致很多Java程序员对于Java类初始化机制不甚了了。下面就Java类成员初始化方式做一个梳理,希望对大家有益。

    一、       所有变量使用前都要得到恰当的初始化

    Java尽力保证:所有变量在使用前都得到恰当的初始化。

    对于类成员变量,Java按照自动初始化的方式,如果是基本成员变量就赋值0,其他赋值null;

    对于方法内部的局部变量,如果未赋值就直接使用的话,Java以编译时错误的形式形成错误报告,告诉你某个变量没有被初始化。

    Java通过这种方式保证变量在使用前都是赋值的,这样就避免了C++中因为忘记变量初始化而产生的错误。

    二、       指定初始化

    Java有一种很直接的赋值方法,就是在定义类成员变量的地方为其赋值(C ++里是不能这样做的,尽管C++新手们总是想这样做)。

       public class InitValue{

          boolean bool = true;

          char ch = 'x';

          short s = 0xff;

          float f = 3.14f;

          double d = 3.1415926;

       }

    甚至可以通过调用方法来提供初始值,如果方法带参数的话,参数必须是之前被初始化了的:

       public class MethodInit{

          int i = f();

          int j = g(i);

          int f(){return 11;};

          int g(intn){return n * 10;};

       }

    注意参数的初始化顺序,如果将int i= f();写到int j = g(i);之后就会出现编译错误,因为初始化j时的参数还没有被初始化,编译器认为这种向前引用是不正确的。

    通过指定初始化,Java类的对象都会具有相同的初始值。如果我们希望构造不同初始化值得对象,可以通过构造函数达到这一目的。

    三、       构造器初始化

    我们可以通过带有参数的构造函数来构造Java 对象,通过输入不同的参数使得Java对象具有不同初始值。但要明确一点:无法阻止自动初始化的进行,它将在构造器被调用之前发生。如下代码:

       public class Counter {

          int i;

          public Counter() {

             i = 10;

          }

       }

    变量域i会被自动初始化为0,然后被构造器赋值为10。

    在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义间,它们仍会在任何方法(包括构造器)被调用之前得到初始化。

    四、       静态数据的初始化

    静态数据属于类,只占用一份存储区域。静态初始化只有在必要时才会进行。当创建对象或者第一次访问静态数据时,它们才会被初始化,此后,静态对象不会再被初始化。

    展开全文
  • 【C++】利用构造函数对类对象进行初始化

    千次阅读 多人点赞 2018-10-06 21:02:00
    运行环境:VS2017 一、对象初始化 每一个对象都应当在它建立之时就有就有确定的内容,否则就会失去对象的意义。...如果一个是公用的,可以在定义对象数据成员进行初始化。 class Time ...
  • C++对象(下)——初始化列表、static成员和友元

    千次阅读 热门讨论 2021-03-08 09:23:18
    C++对象——初始化列表、static成员和友元一、再谈构造函数1.1 构造函数整体赋值1.2 初始化列表三级目录 关于C++对象的学习 C++对象(上)——的基本概念、的限定符及封装和类成员函数的this指针 C++...
  • JAVA笔记-初始化对象初始化

    千次阅读 2016-06-26 13:19:24
    初学Java,觉得初始化对象初始化这一块真的特别重要,翻了很多大神前辈的整理资料,还是有些懵懂,决定将资料整理下,也希望后来的初学者有些许帮助。 上图为的生命周期 看到 方法时,有些懵懂,...
  • 模版静态成员初始化

    千次阅读 2014-08-07 14:51:10
    对类模版中静态数据成员初始化方式进行了详细介绍,对于不同静态成员讨论了初始化方式和一些实现细节。
  • C++ 类对象初始化顺序

    千次阅读 2018-05-16 17:02:58
     基类构造函数如果有多个基类,则构造函数的调用顺序是某派生表中出现的顺序而不是它们在成员初始化表中的顺序;4. 成员类对象构造函数如果有多个成员类对象,则构造函数的调用顺序是...
  • C++类成员变量初始化位置

    千次阅读 2015-09-08 11:29:16
    static: static表示的是静态的。的静态成员函数、静态成员变量是和相关的,而... 在C++中,static静态成员变量不能在的内部初始化。在的内部只是声明,定义必须在定义体的外部,通常在的实现文件中初始
  • 会先创建Stu,对象成员,然后再创建banji 然而我们如果在Banji的构造函数里面初始化Stu,那就来不及了! 这时候,我们应该使用初始化列表! 因为初始化列表在构造函数的函数体执行顺序之前! 能够在初始...
  • C++成员和数据成员初始化总结

    万次阅读 2013-05-24 22:15:38
    C++为中提供类成员初始化列表。 类对象的构造顺序是这样的: 1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员 2.进入构造函数后在构造函数中执行一般计算  1.里面的任何成员变量在定义时是...
  • 深入理解Java对象的创建过程:初始化与实例化

    万次阅读 多人点赞 2017-05-18 14:17:45
    在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。...本文试图JVM执行类初始化和实例化的过程做一个详细深入地介绍,以便从Java虚拟机的角度清晰解剖一个Java对象的创建过程。
  • c++中类对象初始化过程

    千次阅读 2012-06-04 17:10:32
    其中初始化分为2步:(1) 成员初始化, 例如成员为对象,就会调用类对象成员的构造函数。 构造函数初始化。 在成员初始化时,如果构造函数有初始化列表,则用用初始化列表的值初始化成员,但是初始化的顺序是...
  • c++模板静态成员初始化

    千次阅读 2019-07-03 10:00:11
    template<class T> class ThreadPool { private: unsigned int max_task_number; } template<class T> unsigned int ThreadPool<T>::max_task_number=0; 按照颜色对应修改就可了。......
  • 成员变量初始化总结

    万次阅读 2015-04-08 11:52:23
    首先把需要初始化成员变量分为几: Ø 一般变量(int) Ø 静态成员变量(static int) Ø 常量(const int ) Ø 静态常量(static const int)  对应的初始化方式是: Ÿ 一般变量可以在初始化列表里或者...
  • 属性、方法、构造方法和自由块都是类中的成员,在创建类的对象时,类中各成员的执行顺序: 1.父类静态成员和静态初始化快,按在代码中出现的顺序依次执行。 2.子类静态成员和静态初始化块,按在代码中出现的顺序...
  • 构造函数初始化时必须... (常量成员)需要初始化const修饰的类成员 3. (引用)需要初始化引用成员数据class A { ... private: int a; }; class C{ C(int b); }; class B : public A { ... private: int a;...
  • 成员变量的初始化顺序

    千次阅读 2018-09-03 11:34:43
    成员变量的初始化顺序只与变量在中的声明顺序有关,与在构造函数中的初始化列表顺序无关。 注意:是与声明顺序有关。 #include&lt;iostream&gt; using namespace std; class A { public: //我们...
  • C++11中数据成员初始化方法详解

    千次阅读 2016-12-31 21:28:38
    C++98为中提供类成员初始化列表。 类对象的构造顺序是这样的:1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员  2.进入构造函数后在构造函数中执行一般计算  1.里面的任何成员变量在定义时是...
  • 往往被初学者忽视的是,C++中的数据初始化是有jia
  • 类成员初始化可以在构造
  • c++成员变量初始化总结

    千次阅读 2019-06-14 17:04:11
    首先把需要初始化成员变量分为几: a.一般变量(int) b.静态成员变量(static int) c.常量(const int) d.静态常量(static const int) 对应的初始化方式是: a.一般变量可以在初始化列表里或者构造...
  • C++11类成员初始化总结

    千次阅读 2014-05-02 00:36:06
    C++为中提供类成员初始化列表。 类对象的构造顺序是这样的: 1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员 2.进入构造函数后在构造函数中执行一般计算  1.里面的任何成员变量在定义时是不...
  • 测试: #include #include #include #include #include #include #include using namespace std; class member1{ public: member1(){ cout !" ; } ~membe
  • c++类成员初始化方式

    万次阅读 2019-07-11 18:59:33
    转载自: ...常用的初始化可能如下: 1)赋值初始化 class Student { public: Student(string in_name, int in_age) { name = in_name; age = in_age; } private : string...
  • 派生不能在成员初始化列表中直接初始化基类的成员 初始化基类成员 构造函数是不可继承的。因此,派生的构造函数必须通过调用基类的构造函数初始化基类成员,不能够在派生初始化列表直接初始化基类的成员...
  • 初始化和赋值内置类型的成员没有什么大的区别,像任一个构造函数都可以。但有的时候必须用带有初始化列表的构造函数: (1) 成员类型是没有默认构造函数的。若没有提供显式初始化时,则编译器隐式使用成员...
  • C++类成员初始化

    千次阅读 2016-03-29 13:00:03
    C++成员初始化C++包括的类成员主要由以下几种: 非静态非常量数据成员 非静态常量数据成员 静态非常量数据成员 静态常量数据成员 静态成员不能在初始化在C++中,的静态成员(static member)必须在内声明...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 519,388
精华内容 207,755
关键字:

对类对象成员的初始化是通过