初始化列表 , 类型转换,友元函数

一.初始化列表

        1.1 什么是初始化列表呢?

        之前我们实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种⽅ 式,就是初始化列表,初始化列表的使⽤⽅式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成 员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式。


        1.2 初始化列表有什么作用呢?       

我们来举个例子

        

        如上图,这就是一个简单的初始化列表,我们通过初始化列表给0和1赋值了。

        但是这些功能明明构造函数就能完成,为什么还要初始化列表呢?

        我们再来举个例子。

        

        我们来看一下这个例子,我们都知道A没有默认构造函数如果我们直接在B类中声明一个类A的实例化,由于A没有默认构造函数,当我们在主函数中实例化B的时候,此时就会报错了,我们还知道我们无法在B类中直接用类A的一个参数的构造方法(前面讲过),此时我们又想在B中实例化A怎么办呢,此时我们就要用到初始化列表了。

        如图我们完成了我们的要求,这是构造函数无法完成的事情。

        

        我们也可以通过给缺省值的方式来完成初始化列表。

        我来举个例子。

        

        我们来看一下这个图,我们都给了每个变量一个缺省值,此时这个时候就会调用系统默认的构造函数了。

        相当于下图的形式

        

        这个就是系统的默认初始化列表了。

        

        这个时候类A 的形参a的值和类B中a,b的值分别是多少呢?

        答案是我们初始化列表所给的值,因为缺省值是我们在没给值的情况下才会使用的一个值。

        指针我们该如何通过初始化列表直接给它空间呢?

        

        就是如图所示了,我们让c指向了一块40字节大小的空间。

        注意:

        每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义 初始化的地⽅。

        引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始 化,否则会编译报错。

        C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的 成员使⽤的。

        尽量使⽤初始化列表初始化,因为那些你不在初始化列表初始化的成员也会⾛初始化列表,如果这 个成员在声明位置给了缺省值,初始化列表会⽤这个缺省值初始化。如果你没有给缺省值,对于没 有显⽰在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显⽰在初始化列表初始化的⾃定义类型成员会调⽤这个成员类型的默认构造函数,如果没有默认构造会编译错误。

        初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆ 关。建议声明顺序和初始化列表顺序保持⼀致。

        


        第一句话的意思是每个变量只能在初始化列表中出现一次,不能在初始化列表中初始化两次或者更多次。

        第二句话没有默认构造函数的类需要在初始化列表中初始化我们已经知道了,为什么引用类型和const修饰的变量一定要在初始化列表初始化呢?

        引用是对象的别名,一旦引用被初始化指向某个对象,它就会一直引用该对象,不能再引用其他对象。也就是说,引用在创建时必须被初始化,之后不能重新赋值,所以必须直接让它指向某个对象,必须在定义的时候直接初始化。

        const 修饰的成员变量表示其值是常量,一旦被初始化就不能再被修改。

        const和引用也是同理,必须在定义的时候直接初始化。

        倒数第二个的意思就是每个变量都需要走初始化列表,如果有缺省值就用缺省值,没有缺省值就用编译器默认的值。

        最后一句话我们用一个例子来说明一下吧。

        

        大家可以猜一下答案。

        

        和你预想的一样吗?

        我来告诉你为什么,初始化列表初始化的顺序是按照变量在类中声明的顺序来决定的,我们先声明了c,所以就会先走用b初始化c的那个初始化列表,再走b的初始化,所以c是随机值,b是1。

        总结:



二.类型转换

        我们最开始的时候学过的隐式类型转换,浮点数和整形之间可以转换,就是先生成一个临时对象,再把值给到需要值得那个。

        

        这个就是,当a给b赋值的时候,会产生一个double类型的临时对象,这个临时对象的值为10,然后通过那个临时对象给b赋的值。

        那么我们自定义类型和内置类型是怎么转换的呢?

        

        这就是一个类型转换的例子,AA aa=1,此时会调用AA类的一个参数的构造函数用1作为参数生成一个AA类型的临时对象(用1初始化了),然后再给aa赋值,此时就发生了类型的转换。

        我有一个问题AA aa=1和AA aa(1)的区别?

        简单来说就是AA aa(1)是直接调用了构造函数完成初始化了,不会产生临时变量,AA aa=1,则是先通过构造函数通过1进行单参数初始化形成一个临时变量,再通过临时变量调用拷贝构造函数对aa进行初始化,但是现在的编译器都优化了,一般也都是直接调用构造函数了,老的编译器才会再调用拷贝构造。

        

        图中为什么报错了呢?

        因为我们上面也说了,临时对象具有常性,引用(&)必须绑定到一个已经存在的对象上,而不能绑定到一个临时对象。类型转换的时候会产生临时对象,不能绑定。

        怎么解决呢?

        我们只需要在前面加一个const即可。

        

        const支持绑定到临时对象上。

        一个参数的我们会了,多个参数的构造函数该怎么使用呢?

        

        没报错,这样是不是就可以了呢?

        当然是不行的,这样虽然编译通过了,但是还是调用的是单参数的,原因是这是个逗号表达式,只取了后者进行了单参数的构造。

        

        下面的加大括号才是正确的。

        这个类型转换有什么作用呢?它和AA aa(1)的区别也不是太大,达成的效果相似,我们来举个例子。

        

        我们加了个Stack类,Push方法需要传入一个AA类型的对象。

        我们上面也看到了,Stack类中的Push方法需要接受一个AA类型的参数,此时我们常规的写法就是主函数中的前三行的形式了,需要先创建对象,再传入参数,而我们的强制类型转换的话,直接就可以用两行实现就是主函数中的第一行和第四行就行了,减少了代码量,直接传入一个参数1,让它自己实现强制类型转换。

        上面都是内置类型转换为自定义类型,下面我们来看看自定义类型之间的转换。

        

        就是这样的形式了,和上面的内置类型差不多,都是类型转换,需要生成临时对象。

C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。

构造函数前⾯加explicit就不再⽀持隐式类型转换。

        

        此时就不支持了。

类类型的对象之间也可以隐式转换,需要相应的构造函数⽀持。

        



三.友元函数

        我们之前也说过友元函数。

        

        此时我们访问不到类A中的a,b变量,但是我们加入友元就可以访问了。

        

        此时就可以访问了。

        

        类也是同理的。

        需要注意的是:友元是单向的,而不是双向的,图中的只能说A是B的朋友,类A中可以访问到类B中的私有内容,但是B不是A的朋友,你在类B中,你是无法访问到类A中的私有内容的。

        看到没,报错了,访问不到类A中的。

        注意:

友元提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类
声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯。
外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。
友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制。
⼀个函数可以是多个类的友元函数。
友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。
友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。
友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是C的友元。
有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤。

四.结束语

           感谢大家的查看,希望可以帮助到大家,做的不是太好还请见谅,其中有什么不懂的可以留言询问,我都会一一回答。  感谢大家的一键三连。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值