类和对象:
1. const运算符重载:
1. const成员函数:
我们来看我们的下面的代码:
我们来看这个,我们的对象使用const进行修饰,然后我们对象d1调用我们的成员函数,然后我们取d1的地址然后传过去,这时候我们的this指针来进行接收,但这时候就出现问题了,因为我们的对象d1是被const修饰的,所以取出来的d1的地址就是 const Data* 类型的,这个类型的话,我们会限制我们的对象的修改,这时候我们的对象是不能被修改的,但是我们的this指针的类型是 Data* const this,这个是我们的成员函数的this指针的类型,这个类型的话,我们的指针指向的对象是可以被修改的,只是指针的指向不能被改变,这时候就有问题了,(这里就出现了权限的放大),所以在这里,我们就要改变我们的成员函数的this指针的类型。
我们怎么修改我们的函数呢?
我们在函数名的后面放上我们的const来修饰。
这时候我们的this指针就是const Data* const this类型的。
当我们把他修改以后:
这个时候我们的普通的对象和我们的const修饰的对象都可以调用我们的函数,我们的权限可以缩小,但是不能放大。
这样是不是就很方便了,
但是我们想一下,我们的所有的函数是不是都需要我们的这个const来修饰呢?
不是的,我们看一下我们的const修饰成员函数的定义:我们的const修饰成员函数其实修饰的是我们的成员函数的隐含的this指针指向的内容,表明我们的这个this指针指向的内容不能被修改;
我们看这个代码,我们这时候是不能给我们的+=和-=函数的后面,写上const的,因为我们的这两个函数都是修改我们的this指针,我们的this指针一定是我们的类实例化的对象。我们修改了this指针,这时候对象里面的成员变量也就一定改变了。
但是不改变自己的都要加上const来修饰,
2. 取地址运算符重载:
我们看我们的解释:取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载;但是一般这两个不需要我们来实现,编译器自动生成的就足够我们的使用了。
我们的这两个函数构成函数重载,我们的这两个函数的参数类型不一样,我们的下面的函数的参数为const Data* 类型的,上面的是Data* 类型的,所以构成函数重载。
我们的下面的函数后面的const修饰的是我们的函数的参数不能被修改,const类型的,上面的没有。
2. 再探构造函数:
之前我们学习的构造函数的初始化成员变量,我们都是在构造函数的函数体内完成的,但是我们的构造函数的初始化我们还有一种方式来实现:我们可以在我们的初始化列表中进行初始化。
我们可以在我们的初始化列表初始化,当然也可以在我们的函数内初始化,这两种方式都是可以的。但是你在初始化列表初始化以后就不能在函数体里面初始化了,我们只能初始化一次。
但是有些成员变量我们是必须要在我们的初始化列表里面进行初始化的。
比如:
我们的const修饰的成员变量我们就必须使用初始化列表进行初始化,因为我们的const修饰的变量我们是不能被修改的,这时候你到函数体里面进行初始化的话,相当于是把一个值赋给另外一个值,相当于是把他进行了修改,我们这时候要使用初始化列表初始化。
我们继续来看一个点:
我们看我们上面的图片:我们的自定义类型的类MyQueue,我们的自定义的类会调用我们自定义的里面的类的默认构造函数,但是我们的栈,它里面的构造函数是需要传参的构造函数,这既不是全缺省的构造函数,也不是无参的构造函数,那他就不是默认构造函数,这时候因为你自己写了构造函数,编译器并不会主动的给你生成一个默认的构造函数,但是这时候你的构造函数需要一个参数才能启动,所以这时候你的自定义的类MyQueue就必须要主动的写构造函数,并且初始化栈的时候要在初始化列表里面初始化来传参。
所以:
我们再来看下一个点:
我们来看我们的这个代码:我们看我们的自定义类型,这时候我们的自定义类型里面的栈是含有默认构造函数的,这时候我们就可以不用写MyQueue构造函数,他就可以自动的调用我们的成员变量的默认构造函数,但是我们这里的自定义类型里面的成员变量还有一个int类型的变量,这时候我们怎么办?难道给MyQueue写一个构造函数初始化int类型的变量吗?不需要的,我们看我们图片中的做法,这个不是赋值初始化,这个是缺省值;(因为我们的这里是成员变量的声明,他还没有真实的产生,我们还没有实例化出对象,这里就不是赋值,这个是我们的缺省值,这样我们就不用写一个构造函数给我们的MyQueue了)。
当然,我们给我们的成员变量写上缺省值时,我们也可以不使用他,我们也可以在初始化列表里面的时候给他初始化,这时候他就不使用缺省值。
刚才我们说const修饰的是必须在初始化列表里面进行初始化的,但是我们可以在成员变量里面直接给他一个缺省值,这时候我们就不需要在初始化列表里面初始化了。(但是其实,我们的底层还是会走初始化列表的,这时候我们在初始化列表里面就是缺省值进行初始化的。)
我们看我们的最后一点,我们的最后一点说的是我们在对我们的成员变量进行初始化的时候,我们要按照我们的类里面我们的成员变量的声明的顺序来进行初始化;
我们来看一个题目:
这个题目的最终答案是D,因为我们看我们的类里面,我们是先生命的_a2,然后声明的_a1,所以我们看初始化列表,我们先看a2,a2是由a1拷贝构造的,但是这个时候我们的a1还没有进行初始化,所以他是一个随机值,所以我们的a2也就是一个随机值,然后我们初始化完a2的时候,我们现在看a1,a1的话是由我们的形参a来进行拷贝构造的,形参a我们传过来是1,所以我们这里的a1就是1。然后我们调用我们的Print函数,这个函数我们是先打印a1,然后再打印a2,所以结果就是D。所以我们的最后一点说的就是我们的成员变量的声明尽量和我们的初始化的列表顺序保持一致
3. 类型转换:
隐式类型转换:
这时候我们的第一个构造函数被explicit修饰,这时候就不允许隐式类型的转换;
没有explicit,这时候就可以了: