java类默认构造函数,类的几个默认构造函数

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

你可能感兴趣的:(java类默认构造函数)