在类中存在空类,但是它的内部是拥有者成员的,并非什么都没有。
类的默认成员函数:
1:构造函数
2:析构函数
3:拷贝构造函数
4:赋值重载
5&6:取地址重载:主要是对普通对象和const对象取地址,一般很少实现。
一:构造函数
构造函数是一个特殊的成员函数,效用与Init()函数类似,对对象起着初始化的作用。
特性:
1:函数名与类名相同。 2:构造函数没有返回值(Ps:不需要写void!)。
3:在对象进行实例化时,编译器会自动调用。
4:构造函数可以进行重载。
调用规则:对象 + (参数列表)。(与普通函数的调用不一致)
PS:在进行调用的时候,如果没有参数,不需要加括号,有参数时,一定要加括号!!!
我们没写的时候,编译器会自动生成构造函数,并进行调用。
一个类可以有多个构造函数,因为其可以进行重载。
构造函数的运行原理:
规则:在一个类中自动调用构造函数时,内置类型不做任何处理,依旧为随机值,自定义类型自动调用其无参的构造函数。
自定义的尽头仍然是内置类型,但是内置类型又不会做出任何处理,为解决隐患,在后来有着一个补丁:在声明的时候给内置对象缺省值。
自定义类型调用其的无参的构造函数,即为默认构造函数,主要有三类:无参构造,全缺省构造,编译器自动生成的构造。
PS:无参并不是指没有参数,而是指没有进行传参的参数。一般情况,一个类中只能有一个默认构造函数。
实践认识:
在具体写代码的时候,一般情况下我们都要显式地写出构造函数,少数情况下可以不用写,如下:
类Date中自己会调用构造函数,所以对于MyQueue不用写显式的构造函数。
二:析构函数
析构函数是对对象中的资源进行清理,效果与Destroy()函数类似,避免内存泄露。
析构函数特性:
1:函数名等于类名加“~"(有取反之意,实际上析构函数的作用刚好与构造函数相反)
2:不进行传参,没有返回值。
3:在对象生命周期结束时,自动调用析构函数对资源进行清理。
4:一个类中只能有一个析构函数,析构函数不可以进行重载。
Ps:在我们写代码时,内存泄露是一个很大的问题,而析构函数的这一自动调用,意义重大。
一般来说,析构函数可以显式地调用,但我们不去显式调用它,充分利用其编译器自动调用这一特性。
当我们不写时,编译器会自动生成析构函数进行调用,处理情形与构造函数一致,对内置类型不做处理,自定义类型调用其析构函数。
析构时的顺序:后构造的先析构
实践认识:
使用情景:
析构函数并不是每一个类都需要自己显式写的,如以下两种情况不需要:
1:类中没有资源需要清理。
2:类中的内置类型成员没有资源需要清理,剩下的均为自定义类型(自定义有着自己的析构函数,可以自动调用)
PS:简单来看,一般对于使用了malloc,fopen,以及new之后的类才需要自己显式写析构函数。
三:拷贝构造函数
拷贝构造是构造函数的一种,它是利用同类型的对象来进行初始化。
特性:
1:拷贝构造是构造函数的一种重载函数。
2:拷贝构造的参数只有一个且必须是类类型对象的引用。若使用传值调用,会造成无穷递归。
3:我们没有给出显式定义,编译器自动生成默认拷贝构造函数。
在进行调用时,有一下两种写法(以日期类为例子)
Date a1(2024,2,1); (1)Date b1(a1); (2)Date b1 = a1;
(1)为什么不可以使用传值?
在进行传参时,首先要用实参来初始化拷贝构造形参,如果用传值类型来进行拷贝构造的话,那么函数就会一直进行拷贝构造,陷入无穷递归当中。(在写代码时,如果使用传值传参,编译器也会直接报错)。
Ps:在进行传参时,最好使用const进行修饰,避免出现顺序写反,影响到原数据的情况。
(2)未显式写出,编译器自动生成默认拷贝构造函数的原理。
编译器在进行拷贝时,内置类型是按照字节序按字节一个一个地进行拷贝的,俗称:“浅拷贝”,而自定义类型是找其拷贝构造函数。
PS:不是每种类都可以直接用默认拷贝构造,比如栈和链表,就要使用“深拷贝”,自行编写拷贝构造函数,否则会造成代码崩溃。
PS:非典型创建栈,只是为了效果明显。
如果是浅拷贝,那么类b与类a中的_arr为同一地址,那么在析构时会进行两次析构,改变b中的同时也会改变a中的,无法达到我们预期中的效果。
实践认识:
1:一般情况下,有析构函数,就要自己写拷贝构造,没有析构函数,就可以直接用默认拷贝构造函数。
2:无资源需要管理,可以不自己显式写拷贝构造函数,如日期类(Date)
3:内置类型不指向资源,剩下均为自定义类型,可以不自己显式写拷贝构造
4:内部如果有指针或则一些值指向资源,需要析构函数进行释放,亦需要拷贝构造进行深拷贝。
PS:一般情况下而言,需要深拷贝的时候我们就去自定义写,可以用浅拷贝时,直接用默认拷贝构造函数即可。