学习目标:
1. 再谈构造函数
2. Static成员
3. 友元
4. 内部类
5. 再次理解封装
学习内容:
一.再谈构造函数(初始化列表)
1.1构造函数体赋值
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
1.2初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
【注意】
1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
(1)引用成员变量
(2) const成员变量
(3)自定义类型成员(该类没有默认构造函数时在显式初始化列表中初始化)
3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
深度剖析初始化列表:
1. 初始化列表本质上可以理解为成员变量被定义的地方,成员既可以在列表也可以在函数体初始化,但如果两个地方都进行初始化,最终以函数体的为准。对于const和引用变量,由于它们必须在定义时初始化,所以必须在初始化列表初始化
2.关于c11的“补丁”,我们在声明成员变量时赋的缺省值,实际上就是对应到默认的初始化列表中了
3.构造函数实际上就是通过初始化列表来进行初始化的,函数体内只是赋初值,默认构造函数的初始化列表,可以实现“补丁”,以及调用自定义类型的默认构造函数。显式定义的构造函数的初始化列表可以和默认的初始化列表混合搭配使用,“显式有就用显式的,没有就用默认里的”,比如显式中没有对某个变量的初始化,那么就可以用默认表里的弥补。
另外,如果自定义类型成员变量没有默认的构造函数,那么默认初始化列表就不管用了,此时必须在显示的初始化列表中通过参数来调用它的带参构造函数,否则会报错
4.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
二.类对象的隐式类型转换
(1)构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用。接收单个参数的构造函数具体表现:
1. 构造函数只有一个参数
2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
3. 全缺省构造函数
分析一下这里的隐式类型转换:
内置类型转换为自定义类型。创造一个A的临时对象,然后再用这个临时对象拷贝构造a。
编译器遇到构造加拷贝构造->会优化成直接构造
注意:
1.对于多参数的构造函数,隐式类型转换也可以用,只需加花括号即可,在图片中也有体现。
2.由于临时对象具有常性,所以如果要引用,必须加const,如:const A& b = 3;
(2)explicit关键字
explicit修饰构造函数,禁止类型转换
三.static成员
1 概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
注意:
1.类中只存储成员变量,不存储静态成员变量和成员函数,前者在静态区
2.不能给缺省值,因为缺省值是给初始化列表的
3.它在静态区,不在初始化列表中,不走初始化列表
4.属于整个类的所有对象的
5.若被私有化,则类外无法使用,这时可以通过静态成员函数来返回它,如下代码:
关于静态成员函数:静态成员函数是不可以调用非静态成员变量的,但非静态成员函数可以调用静态成员函数,是因为静态成员函数的参数不包含this指针,因此无法调用非静态成员变量。
2.特性总结
1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制
四.友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元分为:友元函数和友元类
1 友元函数
问题:现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。(在全局域的函数通过交朋友可以使用类中的成员变量)
说明:
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰(没有this)
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同
2 友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
注意:
(1)友元关系是单向的,不具有交换性(我是你的朋友,但你不一定是我的朋友)
(2)友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。
(3)友元关系不能继承,在继承位置再给大家详细介绍。
五.内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系