精华内容
下载资源
问答
  • (若文章有不正之处,或难以理解的地方,请多多谅解,欢迎指正)构造函数与默认构造函数构造函数构造函数,主要是用来在创建对象时初始化对象,一般会跟new运算符一起使用,给对象成员变量赋初值。cl...

    前言

    我们知道在创建对象的时候,一般会通过构造函数来进行初始化。在Java的继承(深入版)有介绍到类加载过程中的验证阶段,会检查这个类的父类数据,但为什么要怎么做?构造函数在类初始化和实例化的过程中发挥什么作用?

    (若文章有不正之处,或难以理解的地方,请多多谅解,欢迎指正)

    构造函数与默认构造函数

    构造函数

    构造函数,主要是用来在创建对象时初始化对象,一般会跟new运算符一起使用,给对象成员变量赋初值。

    class Cat{

    String sound;

    public Cat(){

    sound = "meow";

    }

    }

    public class Test{

    public static void main(String[] args){

    System.out.println(new Cat().sound);

    }

    }

    运行结果为:

    meow

    构造函数的特点

    构造函数的名称必须与类名相同,而且还对大小写敏感。

    构造函数没有返回值,也不能用void修饰。如果跟构造函数加上返回值,那这个构造函数就会变成普通方法。

    一个类可以有多个构造方法,如果在定义类的时候没有定义构造方法,编译器会自动插入一个无参且方法体为空的默认构造函数。

    构造方法可以重载。

    等等,为什么无参构造函数和默认构造函数要分开说?它们有什么不同吗?是的。

    默认构造函数

    我们创建一个显式声明无参构造函数的类,以及一个没有显式声明构造函数的类:

    class Cat{

    public Cat(){}

    }

    class CatAuto{}

    然后我们编译一下,得到它们的字节码:

    efb7d1e15610f910568c59198c7b3588.png

    在《Java的多态(深入版)》介绍了invokespecial指令是用于调用实例化方法、私有方法和父类方法。我们可以看到,即使没有显式声明构造函数,在创建CatAuto对象的时候invokespecial指令依然会调用方法。那么是谁创建的无参构造方法呢?是编译器。

    从前文我们可以得知,在类加载过程中的验证阶段会调用检查类的父类数据,也就是会先初始化父类。但毕竟验证父类数据跟创建父类数据,从动作的目的上看二者并不相同,所以类会在java文件编译成class文件的过程中,编译器就将自动向无构造函数的类添加无参构造函数,即默认构造函数。

    为什么可以编译器要向没有定义构造函数的类,添加默认构造函数?

    构造函数的目的就是为了初始化,既然没有显式地声明初始化的内容,则说明没有可以初始化的内容。为了在JVM的类加载过程中顺利地加载父类数据,所以就有默认构造函数这个设定。那么二者的不同之处在哪儿?

    二者在创建主体上的不同。无参构造函数是由开发者创建的,而默认构造函数是由编译器生成的。

    二者在创建方式上的不同。开发者在类中显式声明无参构造函数时,编译器不会生成默认构造函数;而默认构造函数只能在类中没有显式声明构造函数的情况下,由编译器生成。

    二者在创建目的上也不同。开发者在类中声明无参构造函数,是为了对类进行初始化操作;而编译器生成默认构造函数,是为了在JVM进行类加载时,能够顺利验证父类的数据信息。

    噢...那我想分情况来初始化对象,可以怎么做?实现构造函数的重载即可。

    构造函数的重载

    在《Java的多态(深入版)》中介绍到了实现多态的途径之一,重载。所以重载本质上也是

    同一个行为具有不同的表现形式或形态能力。

    举个栗子,我们在领养猫的时候,一般这只猫是没有名字的,它只有一个名称——猫。当我们领养了之后,就会给猫起名字了:

    class Cat{

    protected String name;

    public Cat(){

    name = "Cat";

    }

    public Cat(String name){

    this.name = name;

    }

    }

    在这里,Cat类有两个构造函数,无参构造函数的功能就是给这只猫附上一个统称——猫,而有参构造函数的功能是定义主人给猫起的名字,但因为主人想法比较多,过几天就换个名称,所以猫的名字不能是常量。

    当有多个构造函数存在时,需要注意,在创建子类对象、调用构造函数时,如果在构造函数中没有特意声明,调用哪个父类的构造函数,则默认调用父类的无参构造函数(通常编译器会自动在子类构造函数的第一行加上super()方法)。

    如果父类没有无参构造函数,或想调用父类的有参构造方法,则需要在子类构造函数的第一行用super()方法,声明调用父类的哪个构造函数。举个栗子:

    class Cat{

    protected String name;

    public Cat(){

    name = "Cat";

    }

    public Cat(String name){

    this.name = name;

    }

    }

    class MyCat extends Cat{

    public MyCat(String name){

    super(name);

    }

    }

    public class Test{

    public static void main(String[] args){

    MyCat son = new MyCat("Lucy");

    System.out.println(son.name);

    }

    }

    运行结果为:

    Lucy

    总结一下,构造函数的作用是用于创建对象的初始化,所以构造函数的“方法名”与类名相同,且无须返回值,在定义的时候与普通函数稍有不同;且从创建主体、方式、目的三方面可看出,无参构造函数和默认构造函数不是同一个概念;除了Object类,所有类在加载过程中都需要调用父类的构造函数,所以在子类的构造函数中,**需要使用super()方法隐式或显式地调用父类的构造函数**。

    构造函数的执行顺序

    在介绍构造函数的执行顺序之前,我们来做个题:

    public class MyCat extends Cat{

    public MyCat(){

    System.out.println("MyCat is ready");

    }

    public static void main(String[] args){

    new MyCat();

    }

    }

    class Cat{

    public Cat(){

    System.out.println("Cat is ready");

    }

    }

    运行结果为:

    Cat is ready

    MyCat is ready

    这个简单嘛,只要知道类加载过程中会对类的父类数据进行验证,并调用父类构造函数就可以知道答案了。

    那么下面这个题呢?

    public class MyCat{

    MyCatPro myCatPro = new MyCatPro();

    public MyCat(){

    System.out.println("MyCat is ready");

    }

    public static void main(String[] args){

    new MyCat();

    }

    }

    class MyCatPro{

    public MyCatPro(){

    System.out.println("MyCatPro is ready");

    }

    }

    运行结果为:

    MyCatPro is ready

    MyCat is ready

    嘶......这里就是在创建对象的时候会先实例化成员变量的初始化表达式,然后再调用自己的构造函数。

    ok,结合上面的已知项来做做下面这道题:

    public class MyCat extends Cat{

    MyCatPro myCatPro = new MyCatPro();

    public MyCat(){

    System.out.println("MyCat is ready");

    }

    public static void main(String[] args){

    new MyCat();

    }

    }

    class MyCatPro{

    public MyCatPro(){

    System.out.println("MyCatPro is ready");

    }

    }

    class Cat{

    CatPro cp = new CatPro();

    public Cat(){

    System.out.println("Cat is ready");

    }

    }

    class CatPro{

    public CatPro(){

    System.out.println("CatPro is ready");

    }

    }

    3,2,1,运行结果如下:

    CatPro is ready

    Cat is ready

    MyCatPro is ready

    MyCat is ready

    通过这个例子我们能看出,类在初始化时构造函数的调用顺序是这样的:

    按顺序调用父类成员变量和实例成员变量的初始化表达式;

    调用父类构造函数;

    按顺序分别调用类成员变量和实例成员变量的初始化表达式;

    调用类构造函数。

    嘶......为什么会是这种顺序呢?

    Java对象初始化中的构造函数

    我们知道,一个对象在被使用之前必须被正确地初始化。本文采用最常见的创建对象方式:使用new关键字创建对象,来为大家介绍Java对象初始化的顺序。new关键字创建对象这种方法,在Java规范中被称为由执行类实例创建表达式而引起的对象创建。

    Java对象的创建过程(详见《深入理解Java虚拟机》)

    当虚拟机遇到一条new指令时,首先会去检查这个指令的参数是否能在常量池(JVM运行时数据区域之一)中定位到这个类的符号引用,并且检查这个符号引用是否已被加载、解释和初始化过。如果没有,则必须执行相应的类加载过程(这个过程在Java的继承(深入版)有所介绍)。

    类加载过程中,准备阶段中为类变量分配内存并设置类变量初始值,而类初始化阶段则是执行类构造器方法的过程。而方法是由编译器自动收集类中的类变量赋值表达式和静态代码块(static{})中的语句合并产生的,其收集顺序是由语句在源文件中出现的顺序所决定。

    其实在类加载检查通过后,对象所需要的内存大小已经可以完全确定过了。所以接下来JVM将为新生对象分配内存,之后虚拟机将分配到的内存空间都初始化为零值。接下来虚拟机要对对象进行必要的设置,并这些信息放在对象头。最后,再执行方法,把对象按程序员的意愿进行初始化。

    cbcf2dbe0464b8d19145c601efa5687d.png

    以上就是Java对象的创建过程,那么类构造器方法与实例构造器方法有何不同?

    类构造器方法不需要程序员显式调用,虚拟机会保证在子类构造器方法执行之前,父类的类构造器方法执行完毕。

    在一个类的生命周期中,类构造器方法最多会被虚拟机调用一次,而实例构造器方法则会被虚拟机多次调用,只要程序员还在创建对象。

    等等,构造函数呢?跑题了?莫急,在了解Java对象创建的过程之后,让我们把镜头聚焦到这里“对象初始化”:

    ccb74cd99921d6c2c19da111b964a2dc.png

    在对象初始化的过程中,涉及到的三个结构,实例变量初始化、实例代码块初始化、构造函数。

    我们在定义(声明)实例变量时,还可以直接对实例变量进行赋值或使用实例代码块对其进行赋值,实例变量和实例代码块的运行顺序取决于它们在源码的顺序。

    在编译器中,实例变量直接赋值和实例代码块赋值会被放到类的构造函数中,并且这些代码会被放在父类构造函数的调用语句之后,在实例构造函数代码之前。

    举个栗子:

    class TestPro{

    public TestPro(){

    System.out.println("TestPro");

    }

    }

    public class Test extends TestPro{

    private int a = 1;

    private int b = a+1;

    public Test(int var){

    System.out.println(a);

    System.out.println(b);

    this.a = var;

    System.out.println(a);

    System.out.println(b);

    }

    {

    b+=2;

    }

    public static void main(String[] args){

    new Test(10);

    }

    }

    运行结果为:

    TestPro

    1

    4

    10

    4

    总结一下,Java对象创建时有两种类型的构造函数:类构造函数方法、实例构造函数方法,而整个Java对象创建过程是这样:

    0094d14df75f2e35fe514d12afb1312b.png

    结语

    现在是快阅读流行的时代,短小精悍的文章更受欢迎。但个人认为回顾知识点最重要的是温故知新,所以采用深入版的写法,不过每次写完我都觉得我都不像是一个小甜甜了......

    .我最大的动力~

    参考资料:

    展开全文
  • 1.构造函数什么是构造函数——构造函数即用来给对象初始化的函数。函数名字与类名相同。构造函数的特性——(1)没有返回值类型(2)在创建对象时由编译器自动调用,且在对象的整个生命周期只被调用一次。(3)构造函数...

    1.构造函数

    什么是构造函数——构造函数即用来给对象初始化的函数。函数名字与类名相同。

    构造函数的特性——(1)没有返回值类型(2)在创建对象时由编译器自动调用,且在对象的整个生命周期只被调用一次。(3)构造函数可以重载。(4)在构造函数被调用完毕后对象才被创建完成。(5)构造函数必须用public修饰,否则无法创建对象

    构造函数的初始化列表——(1)对象的各数据成员在初始化列表中才是真正的初始化,在函数体中进行的叫赋值。(2)数据成员中必须确定进行初始化的数据都必须放在初始化列中进行初始化。比如const类型数据,引用类型数据,其他类类型成员,且类的构造函数是非缺省的。(关于最后一个例子,我认为的原因是,如果包含的类显示定义了非缺省构造函数,那么就会导致在初始化这个类的对象时无法调用默认构造函数,因此必须对它进行初始化,而不能像T a那样调用默认构造函数然后在构造函数体中进行赋值)(3)初始化列表变量初始化的顺序不是按照列表中的顺序来,而是根据数据成员在类中的声明顺序来进行初始化。(4)在定义构造函数时不写初始化列表不代表就没有初始化列表,编译器会自动生成一个初始化列表,只不过会用随机值对数据成员进行初始化。这点也说明了为什么有些类型数据成员必须显示的在初始化列表中进行初始化。

    默认构造函数——(1)如果在类中没有声明构造函数,则编译器会自动生成一个无参默认构造函数。但如果程序员给出了任何一种构造函数,编译器都不会再默认生成。(2)无参构造函数和全缺省构造函数都称为默认构造函数,且两者不能共存,并不能形成重载。(3)虽然C++语法规定编译器需要给没有显示定义构造函数的类创建一个默认构造函数,但是如果类的数据成员只是基础数据类型,则编译器不会给类生成默认的构造函数,因为即便生成默认的构造函数,也是在初始化列表中初始化随机值,但是如果数据成员中有别的类类型成员,且那个类中显示定义了缺省构造函数,编译器才会给本类生成默认的构造函数,如果在另一个的类中显示定义了非全缺省的构造函数,则编译器会报错“无法引用构造函数”,如果另一个类也没有显示定义构造函数,则本类同样不会自动生成默认构造。(4)编译器什么时候一定会生成默认构造:1.数据成员有别的类类型且显示定义了默认构造函数。2.继承位置  3.虚拟继承  4.类中有虚函数。

    构造函数不能被哪些关键词修饰——(1)const(2)static(3)virtual

    2.析构函数

    什么是析构函数——析构函数用来清理对象资源,在对象被销毁时被自动调用。

    析构函数的特性——(1)名字与类名字相同,在前面加一个“~”,没有返回值类型。(2)不可以重载(3)没有参数。(4)被编译器自动调用,一般不通过对象显示调用。(5)类中没有涉及到资源管理时,析构函数不需要显示给出,涉及到资源管理时,需要程序员显示给出析构函数进行资源回收。

    析构函数什么时候被调用——(1)如果在静态存储区,则在程序结束时被调用。(2)如果创建的是自动存储类对象,在程序执行完对应的代码块后调用。(3)如果是用new创建的对象,则在使用delete时调用。(4)程序有时会创建临时对象来执行特定的操作,例如返回值时。那么会在程序结束该对象的使用时调用。

    默认析构函数——没有显示定义析构函数时编译器会默认给出析构函数。

    3.拷贝构造函数

    什么是拷贝构造函数——创建对象时可以用其他的对象来初始化这个对象,拷贝构造函数的作用就是通过另一个同类对象来初始化对象。

    拷贝构造函数的特性——(1)没有返回值类型(2)参数为const类类型&,不能传值,因为如果传值,则在调用拷贝构造时,会在栈上创建临时对象来保存实参对象,此时又会调用拷贝构造来对临时对象进行初始化,此时就会造成无限递归。(3)可以算作构造函数的一种重载。

    默认拷贝构造函数——(1)同构造函数相似,对于数据成员只有内置类型的类,在没有显示定义构造函数的情况下,编译器不会自动生成默认拷贝构造函数,而是直接选择进行赋值,原因在于免除函数调用的开销。但是如果数据成员中包含其他类类型,在其他类类型成员的类中如果显示定义了拷贝构造函数,则会生成默认的拷贝构造去调用其他类的拷贝构造函数,如果其他类没有显示定义拷贝构造,则两个类都不会生成默认构造函数,都直接采用汇编中的mov。

    浅拷贝与深拷贝——由编译器默认生成的拷贝构造函数(或者说没有生成)执行的是浅拷贝,即只是将对象所在的内存中的内容拷贝到另一个对象内存空间中,如果被拷贝的对象内存空间中保存的是一个指针类型数据,那么钱拷贝的结果只是拷贝了这个地址,结果就是导致两个对象使用了同一份指针指向的资源,在释放资源时就会导致同一个指针被释放了两次,且其中一个对象修改了资源也会导致另一个对象的资源改变。因此在有资源管理的情况下,应当显示定义拷贝构造函数并进行深拷贝。

    写时拷贝——浅拷贝+计数。计数会计算当前使用一个资源的对象的个数,不同的对象只保存该资源的地址,在对象释放资源时,对计算减1,如果结果还大于0,则不释放资源,如果等于0,则代表已经没有其他对象使用这个资源,可以进行释放。在修改资源时,进行深拷贝,使用另一份资源,保证不会修改其它对象的资源。

    4.赋值运算符重载函数

    什么是赋值运算符重载函数——两个已经创建的对象,可以进行赋值操作。重新定义赋值运算符的操作,使赋值运算符作用于这个类的对象时进行程序员想要的操作。

    默认的赋值运算符重载函数——与默认构造函数类似,如果类中数据成员没有其他类成员,则编译器不会生成默认此函数,直接使用mov,在有其他类类型成员的情况下,如果其他类中没有显示定义赋值运算符重载函数,则此类同样不会默认生成此函数,其他类中显示定义了赋值运算符重载函数,此类才会生成默认的此函数。

    赋值运算符重载函数的实现——通常的格式是,T& operator=(T& 形参名),(1)为什么只有一个形参,因为有一个隐藏的this指针不需要程序员显示声明。(2)为什么形参类型要用&,避免值拷贝,效率更高。(3)为什么要有返回值,为了可以进行连续赋值。(4)为什么返回值类型要用引用且通常return *this,为了符合连续赋值的规则,即假设int a,b,c,a=b=c,赋值的规则是将c的值赋值给b,再用b的值赋值给a。b=c相当于b.operator=(c),因此函数必须返回*this才是返回b本身。

    前置++/--,后置++/--的运算符重载——前置++/--直接返回对象本身(返回值类型为引用)即可。后置则要进行值拷贝,因为函数实现需要先创建一个局部对象保存原对象的值,然后返回给调用的对象,由于局部对象在函数结束后会被回收,因此不能返回引用,在返回临时对象的值以后,再对原对象进行++操作即可实现后置++的特性。后置++/--会在函数形参列表加一个"int"以示与前置的区别。T operator++/--(int)。虽然前置的原型为T& operator++/--(),但是返回值无法作为函数重载的特征。

    哪些运算符不能被进行运算符重载——.    .*    :?    ::    sizeof。

    运算符重载要注意什么——(1)不能重载不存在的运算符。(2)在定义函数时要使其结果与运算符意义一致,不能重载+,而实现-运算。

    5.小结(本文许多说法不一定正确,是我自己的总结)

    (1)为什么C++类会生成这6种默认成员函数,我认为是为了使自定义类型的使用和内置类型的使用相似。 C++的目标是使得使用类与使用基本的内置类型(如char,int)尽可能相同。

    (2)小结一下构造函数,拷贝构造,赋值运算符重载。这三者总的来说其实就是致力于修改对象的内存空间,所以为什么编译器有时会自动生成它们,有时又不会,总的思想就是,我现在要修改一个对象的内存空间,如果你没有给我修改的方案,即显示定义对应的函数,那么我就直接按照自己的方法进行操作就好,没必要再创建一个函数,再把我操作的方法写入函数中,再调用这个函数。但是如果你给了我修改的方案,那么我就得调用你给的函数,为了调用你给的函数,我必须自动生成对应的函数来调动你的函数。

    展开全文
  • 默认构造函数 默认构造函数:是无参调用的构造函数,包括两种: 没有参数 每个参数有初始值 默认构造函数在默认初始化和值初始化中得到调用。 默认初始化:在不使用初始化器构造变量时执行的初始化。 值初始化:...

    由来

    先看下面代码
    在这里插入图片描述
    代码内容很简单,定义了一个包含成员x,y的类Point。在序列的地方可以使用这个类
    在这里插入图片描述
    虽然我们并没有定义Point类的构造函数,我们依然可以定义Point类的pt对象并使用它,其原因是编译器会自动生成一个缺省的构造函数,其效果相当于
    在这里插入图片描述
    但是,一旦添加了其他有参数的构造函数,编译器就不再生成缺省的构造函数了
    在这里插入图片描述

    C++11的解决方案

    C++11允许我们使用=default来要求编译器生成一个默认构造函数:
    在这里插入图片描述
    这样,我们就可以继续我们的美好生活了。

    #include <stdio.h>
    
    struct Point {
        Point() = default;
        Point(int _x, int _y) : x(_x), y(_y){}
        int x;
        int y;
    };
    
    
    int main() {
        Point point;
        printf("%d, %d", point.x, point.y);
    }
    

    在这里插入图片描述

    如果是自己编写的无参构造函数的话,就需要指定成员的构造方式。默认构造函数会对数据成员进行默认初始化,不需要另外指定。这样可以省去一些麻烦。

    由于整数是内置类型,而整数成员的默认初始化是不初始化,所以本例中的x,y还是需要类内初始化。这是另一个话题。

    定义

    默认构造函数:是无参调用的构造函数,包括两种:

    • 没有参数
    • 每个参数有初始值
    class Box {
    public:
        Box() { /*执行任何必需的默认初始化步骤*/}
        //所有参数都有默认值
        Box (int w = 1, int l = 1, int h = 1): m_width(w), m_height(h), m_length(l){}
    ...
    }
    

    调用场合

    默认构造函数在默认初始化值初始化中得到调用。

    • 默认初始化:在不使用初始化器构造变量时执行的初始化。
    • 值初始化:在以空初始化器构造对象时进行的初始化

    说句人话即使:如果构造函数在未指定参数或者提供了一个空初始化器列表,则会调用默认构造函数:

    vector v1;
    vector v2{};
    

    说明

    默认构造函数是一种特殊的成员函数。如果未在类中声明任何构造函数,则编译器将提供隐式的inline默认构造函数

    #include <iostream>
    using namespace std;
    
    class Box {
    public:
        int Volume() {return m_width * m_height * m_length;}
    private:
        int m_width { 0 };
        int m_height { 0 };
        int m_length { 0 };
    };
    
    int main() {
        Box box1; //调用编译器生成的构造函数
        cout << "box1.Volume: " << box1.Volume() << endl; // Outputs 0
    }
    

    如果声明了任何非默认构造函数、编译器不会提供默认构造函数

    class Box {
    public:
        Box(int width, int length, int height)
            : m_width(width), m_length(length), m_height(height){}
    private:
        int m_width;
        int m_length;
        int m_height;
    
    };
    
    int main(){
    
        Box box1(1, 2, 3);
        Box box2{ 2, 3, 4 };
        Box box3; // C2512: no appropriate default constructor available
    }
    

    如果类中没有默认构造函数,将无法通过单独使用方括号语法来构造该类的对象数组。 例如,在前面提到的代码块中,框的数组无法进行如下声明:

    Box boxes[3]; // C2512: no appropriate default constructor available
    

    但是,可以使用一组初始值设定项列表来初始化 Box 对象的数组:

    Box boxes[3]{ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
    

    语法

    语法:

    语法说明o
    类名 ( ) ;(1)
    类名 :: 类名 ( ) 函数体(2)
    类名() = delete ;(3)C++11起
    类名() = default ;(4)C++11起
    类名 :: 类名 ( ) = default ;(5)C++11起

    其中类名 必须指名当前类(或类模板的当前实例化),或在命名空间作用域或友元声明中声明时,必须是有限定的类名。

    1. 类定义中的默认构造函数声明
    struct Point {
        Point(){  x = 0;  y = 0;};
        int x;
        int y;
    };
    
    1. 类定义之外的默认构造函数的定义(该类必须包含一条声明1)
    struct Point {
        Point();  //声明1
        int x;
        int y;
    };
    Point::Point(){
        x = 10;
        y = 0;
    };
    
    1. 弃置的默认构造函数:若其被重载决议所选择,则程序编译失败。
    struct Point {
        Point() = delete ;  // 错误:使用了被删除的函数‘Point::Point()’
        int x;
        int y;
    };
    
    1. 预置的默认构造函数:即便其他构造函数存在,在某些情况下编译器会定义的隐式默认构造函数。
    #include <stdio.h>
    
    struct Point {
        Point() = default;
        Point(int _x, int _y) : x(_x), y(_y){}
        int x;
        int y;
    };
    
    
    int main() {
        Point point;
        printf("%d, %d", point.x, point.y);
    }
    

    在这里插入图片描述

    1. 类定义之外的预置的默认构造函数(该类必须包含一条声明 (1))。这种构造函数被当做是用户提供的(user-provided)(见下文以及值初始化)。
    struct Point {
        Point() ;  // 错误:使用了被删除的函数‘Point::Point()’
        int x;
        int y;
    };
    
    Point::Point() = default;
    

    隐式声明的默认构造函数

    • 什么叫做隐式声明:用户没有声明、编译器声明
    • 若不对类类型(struct、class 或 union)提供任何用户声明的构造函数,则编译器将始终声明一个作为其类的 inline public 成员的默认构造函数。
    #include <stdio.h>
    
    struct Point1 {
        int x;
        int y;
    };
    
    class Point2 {
    public:
        int x;
        int y;
    };
    union Point3 {
        int x;
        int y;
    };
    
    int main() {
        Point1 point1;
        Point2 point2;
        Point3 point3;
        printf("%d, %d\n", point1.x, point1.y);
        printf("%d, %d\n", point2.x, point2.y);
        printf("%d, %d\n", point3.x, point3.y);
    }
    

    在这里插入图片描述

    • C++11起,当存在用户声明的构造函数时,用户仍可以关键词 default 强制编译器自动生成原本隐式声明的默认构造函数。
    #include <stdio.h>
    
    struct Point1 {
        Point1() = default;  // 必须,当用户最定义了有参构造函数时编译器不会自己生成默认构造函数
        Point1(int _x, int _y) : x(_x), y(_y){}
        int x;
        int y;
    };
    
    class Point2 {
    public:
        Point2() = default; // 必须,当用户最定义了有参构造函数时编译器不会自己生成默认构造函数
        Point2(int _x, int _y) : x(_x), y(_y){}
        int x;
        int y;
    };
    union Point3 {
        Point3() = default; // 必须,当用户最定义了有参构造函数时编译器不会自己生成默认构造函数
        Point3(int _x) : x(_x){}
        int x;
        int y;
    };
    
    int main() {
        Point1 point1;
        Point2 point2;
        Point3 point3;
        printf("%d, %d\n", point1.x, point1.y);
        printf("%d, %d\n", point2.x, point2.y);
        printf("%d, %d\n", point3.x, point3.y);
    }
    

    在这里插入图片描述

    • 隐式声明(或在其首个声明被预置)的默认构造函数,具有动态异常说明 (C++17 前)异常说明 (C++17 起)中所描述的异常说明。

    其他

    内置类型被认为具有默认构造函数和拷贝构造函数。但是,对于内置类型的未初始化的非static变量,其默认构造函数不会被调用。内置整数类型的默认值为0,浮点数默认值为0.0,指针类型的默认值为nullptr

    void f(){
    	int a0;       //未初始化
    	int a2{};     //初始化为0
    	double d1{};  //初始化欸0.0
    	char *p{};    //p变成nullprt
    
    	int *p1 = new int;   //未初始化
    	int *p2 = new int{}; //初始化为0
    }
    

    内置类型的构造函数最常用于模板参数:

    template<typename T>
    struct Handle{
    	T * p;
    	Handle(T *pp = new T{}) :p{pp}{}
    };
    
    Handler<int> px;  //会生成int{} --- 初始化为0
    

    引用和const必须被初始化。因此,一个包含这些成员的类不能默认构造,除非程序员提供了类内成员初始化器或者定义了一个默认构造函数来初始化它们

    int glob{9};
    
    struct X{
        const int a1{7};  //ok
        const int a2;     //错误:需要一个用户自定义构造函数
        const int & r{9};  //ok
        int& r1{glob};    //ok
        int& r2;          //错误,需要一个用户自定义构造函数
    };
    
    X x;            //错误:X没有默认构造函数
    

    实例

    class A
    {
    public:
    	A();  //没有参数
    };
    class B
    {
    public:
    	explicit B(int x = 1, bool b = true);  //每个参数有初始值
    	//explicit:阻止执行隐式转换,但是可以显示类型转换
    };
    class C
    {
    public:
    	explicit C(int c);  //非默认构造函数
    };
    
    
    struct A
    {
        int x;
        A(int x = 1): x(x) {} // 用户定义默认构造函数
    };
     
    struct B: A
    {
        // 隐式定义 B::B(),调用 A::A()
    };
     
    struct C
    {
        A a;
        // 隐式定义 C::C(),调用 A::A()
    };
     
    struct D: A
    {
        D(int y): A(y) {}
        // 不会声明 D::D(),因为存在另一构造函数
    };
     
    struct E: A
    {
        E(int y): A(y) {}
        E() = default; // 显式预置,调用 A::A()
    };
     
    struct F
    {
        int& ref; // 引用成员
        const int c; // const 成员
        // F::F() 被隐式定义为弃置
    };
     
    int main()
    {
        A a;
        B b;
        C c;
    //  D d; // 编译错误
        E e;
    //  F f; // 编译错误
    }
    

    https://author.baidu.com/home?from=bjh_article&app_id=1597683592389912

    等待研究

    展开全文
  • Java默认构造函数

    2021-03-26 14:51:21
    如果您未指定任何构造函数,则编译器将为您生成一个默认构造函数。这意味着以下两个片段在语义上是等效的:publicclassTestClass{privateStringtest;}publicclassTestClass{privateStringtest;publicTestClass(){}}...

    示例

    构造函数的“默认”是它们没有任何参数。如果您未指定任何构造函数,则编译器将为您生成一个默认构造函数。

    这意味着以下两个片段在语义上是等效的:

    public class TestClass {

    private String test;

    }public class TestClass {

    private String test;

    public TestClass() {

    }

    }

    默认构造函数的可见性与类的可见性相同。因此,包私有定义的类具有包私有默认构造函数

    但是,如果您具有非默认构造函数,则编译器将不会为您生成默认构造函数。所以这些不是等效的:

    public class TestClass {

    private String test;

    public TestClass(String arg) {

    }

    }public class TestClass {

    private String test;

    public TestClass() {

    }

    public TestClass(String arg) {

    }

    }

    请注意,生成的构造函数不会执行任何非标准的初始化。这意味着您的类的所有字段都将具有其默认值,除非它们具有初始化程序。

    public class TestClass {

    private String testData;

    public TestClass() {

    testData = "Test"

    }

    }

    构造函数的名称如下:

    TestClass testClass = new TestClass();

    展开全文
  • 在本文中,我们将学习默认构造函数,什么是默认构造函数,它们如何定义以及如何调用?默认构造函数或零参数构造函数默认构造函数也称为零参数构造函数。默认构造函数没有任何参数,用于设置(初始化)类数据成员。由于...
  • Java中的默认构造函数将类的数据成员初始化为其默认值,例如0表示int,0.0表示double等。如果用户没有为该类实现显式构造函数,则Java编译器默认实现此构造函数。演示此过程的程序如下:示例classNumberValue{...
  • Java中的默认构造函数

    2021-02-12 10:12:37
    java中如果在一个类中没有写明任何构造函数的,那么会存在一个无参的构造函数,如下:public class Children {private String name;private String address;public String getName() {return name;}public void setName...
  • 屏蔽掉系统自带的 默认构造函数 如果用户自己没有定义构造函数,那么,C++系统会自动生成一个构造函数,只是,这个构造函数的函数体是空的,也没有参数,不执行初始化操作。 例如,定义一个C++类,类名是 student...
  • 根据亚历山德鲁卡尼13的回答。这是我的钱。在import yamlYAMLObjectTypeRegistry = {}def register_type(target):if target.__name__ in YAMLObjectTypeRegistry:print "{0} already in registry.".format(target.__...
  • c++中默认构造函数

    2021-05-06 15:42:06
    编译器什么时候会产生默认构造函数? 类成员中有成员是类对象,并且该成员的类含有默认构造函数,那么编译器会帮你给这个类也生成一个默认构造函数,用来调用其他成员对象的构造函数,完成该成员的初始化构造。如果...
  • Pretty easy question, but anyway: is there any reason to override default constructor like this:public SomeObject(){}It is public. It is does not have any logic. So, is there necessary?...
  • 默认构造函数默认构造函数是0参数构造函数,其中包含对超类构造函数的无参数调用。将默认值分配给新创建的对象是默认构造函数的主要职责。仅当程序未在类中写入任何构造函数时,编译器才会在代码中写入默认构造函数...
  • /*** Copyright 2008-2009 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in* compliance with the License....
  • 【C++】4-1.14 class默认构造函数1.默认构造函数2.使用场景3.使用示例 1.默认构造函数 默认构造函数是可以无参调用的构造函数; 默认构造函数可以是定义为空参数列表的构造函数,也可以是所有参数都有默认参数值的...
  • 我在学加法运算符重载,我在类person里建了一个返回...但是报错了,错误为:类 "person" 不存在默认构造函数 解决方法:在person类中先创建个无参的构造函数类型; 有了这个类型你才能创建这种类型的对象; ...
  • C++的默认构造函数

    2021-04-08 11:06:11
    默认构造函数:无需提供初始值(或者说无参数)的构造函数 如果没有定义任何构造函数,则编译器会自动定义默认构造函数。 编译器自动提供的默认构造函数的就是一个空函数,什么操作也不做,只是为了满足程序的运行...
  • 对于类,我们知道数据部分的访问...构造函数的原型和函数头有一个有趣的特性——虽然没有返回值,但没有被声明为void型,实际上,构造函数没有声明类型。 构造函数的方法:(举个例子) 原函数: void Stock::acqui
  • 和C ++一样,如果没有用户编写的默认或参数化构造函数,Java会自动创建默认构造函数,并且(如C ++),默认构造函数会自动调用父类默认构造函数。但与C ++不同,Java中的默认构造函数将成员数据变量初始化为默认值...
  • 您必须向基类中添加一个no-args构造函数,仅此而已!publicabstractclassA{privateStringname;publicA(){this.name=getName();}publicabstractStringgetName();publicStringtoString(){return"simpleclas...
  • //invokes A's default constructor } } 你可以看到最终将链接Object的默认构造函数,然后是A的默认构造函数,最后是B的默认构造函数. >那么什么时候超类没有无参数构造函数呢? 当您明确定义一个时,它将没有no-arg...
  • Java中的默认构造函数:构造函数在创建对象时会对其进行初始化。它与类具有相同的名称,并且在语法上类似于方法。但是,构造函数没有显式的返回类型。构造函数有两种类型,即-参数化构造函数-带参数的构造函数。无...
  • 指定参数 ... print("这是构造函数里的内容 这个方法在实例化的时候触发"); } void printInfo() { print("${this.name}------${this.age}"); } } void main() { Person p1 = new Pe...
  • vs2019中,以下代码,有时会报错:没有合适的默认构造函数可用 class Person { public: Person(string name, int age, int height) { m_name = name; m_age = age; m_height = height; } string m_name...
  • g给了我以下错误: simpleWindow.cpp:49: error: expected primary-expression before ‘temp’ simpleWindow.cpp: In member function ‘bool SimpleWindow::drawText(std::string)’: 如何在不使用默认构造函数的...
  • 它没有构造函数或析构函数(除了隐式的,也是微不足道的),复杂的成员等.它只是一个带有标量或其他POD类型成员的结构.当你这样做:struct Child : Parent{int b;Child () {printf("child constructor\n");}}你只是构建...
  • 子类的默认成员函数 … … … 前面我们说过,类共有6个默认成员函数,如果不写那么编译器就会我们自动生成 那么对于子类呢?子类继承了父类,它该如何处理默认成员函数 我们创建下面的父类Person和子类Student讲解 ...
  • 沧海一幻觉编译器会自动生成默认构造函数吗?隐式生成的默认构造函数是否执行零初始化?如果您从法律上解析2003年标准的语言,则答案是肯定的,否。但是,这还不是全部,因为与用户定义的默认构造函数不同,从头创建...
  • 假设一个定义类的默认构造函数,然后构造这个类的对象数组将是一个问题。从而实现遗嘱new运营商掰开使用代码。首先,分配内存,然后调用构造函数对象分配内存。的代码做一个简单的备忘。//未定义构造函数的类不能...
  • //告诉编译器执行默认构造函数 Person(int age) { this->m_age = age; } private: int m_age; }; int Person::sum = 10;//类外初始化 int main() { Person p1;//可以使用默认构造 return 0.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 769,433
精华内容 307,773
关键字:

默认构造函数