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

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

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



正确应该这样

4.如果构造函数的初始化列表里没写这个成员,就用声明时的缺省值;如果初始化列表里写了,就用初始化列表的值。
初始化列表里写了:

初始化列表没写:


5.尽量走初始化列表
- 所有成员一定走初始化列表,你不写编译器也会补。
- 初始化列表优先级 > 类内缺省值。
- 内置类型:没初始化、没缺省值 → 值不确定。
- 自定义类型:没初始化 → 调用默认构造,没有就报错。
- 引用 /const/ 无默认构造的对象:必须手动写初始化列表。

看下一段代码
lass Time
{
public:
Time(int hour)
: _hour(hour)
{
cout << "Time()" << endl;
}
private:
int _hour;
};
class Date {
public:
Date()
: _month(2)
{
cout << "Date()" << endl;
}
void Print() {
cout << this->_year << "-"
<< this->_month << "-"
<< this->_day << endl;
}
private:
int _year;
int _month=5;
int _day=30;
//int& _x;
const int _y=1;
Time _t;
};
所有成员都走了初始化列表,但是Time类型没有合适的默认构造最后报错

6.初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆关。
7.建议声明顺序和初始化列表顺序保持⼀致。
接下来我们来看一道题目
#include<iostream>
using namespace std;
class A
{
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2 = 2;
int _a1 = 2;
};
int main()
{
A aa(1);
aa.Print();
}
你觉得这段代码的运行结果会是什么?

正确答案是D
类中按照声明顺序进⾏初始化,先声a2,然后是a1,初始化列表优先级 > 类内缺省值。
a2先拿a1的值,但是此时a1还没有值,所以a2 值不确定。
a1为传进来a的值,所以a1为1.
初始化列表总结:
⽆论是否显⽰写初始化列表,每个构造函数都有初始化列表;
⽆论是否在初始化列表显⽰初始化成员变量,每个成员变量都要⾛初始化列表初始
类型转换
我们在c语言的学习中,接触过一些类型转换比如整型和浮点型之间的转换
在c++中也有类型的转换,其中就包括一下三种。
1.C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。

整形现在不能和类类型转换,因为没有相关内置类型为参数的构造函数。
应该这样就可以


接下来我们看

你觉得这个可以编译通过吗?
答案是不可以
对比上面就是多了引用,按理来说应该可以通过,但是不要忘记了,1是创造了一个临时变量,然后临时变量有有常性,要加const,之前那篇讲引用的时候讲到过,可以看下面这个图理解一下
那为什么c++要搞这个呢
就是为了类类型来准备

多参数的隐式类型转换要用花括号

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


3.类类型的对象之间也可以隐式转换,需要相应的构造函数⽀持。
class B
{
public:
B(const A& a)
:_b(a.Get())
{
}
private:
int _b = 0;
};
B类型和A类型产生了联系,也可以使用隐式类型转换

static成员
1.⽤static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进⾏初始化。静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区

⽤static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。

2.静态成员函数:
为什么不能访问非静态成员?
- 非静态成员:属于每一个对象,每个对象都有自己独立的一份
- 静态成员:属于整个类,所有对象共享一份
- this 指针:指向当前调用函数的那个对象。
- 静态成员函数是属于类、不属于任何对象的,所以它没有 this 指针。没有 this 指针,就不知道要访问哪个对象的非静态成员,编译器直接报错

3.突破类域就可以访问静态成员,可以通过类名::静态成员 或者 对象.静态成员 来访问静态成员变量 和静态成员函数。
4.静态成员也是类的成员,受public、protected、private 访问限定符的限制。

因为私有无法从外部直接访问。
5.静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员 变量不属于某个对象,不⾛构造函数初始化列表。
3.友元
- 正常情况下:类外不能访问 private 成员
- 友元作用:给外部函数 / 类开 “后门”,让它能直接访问你的私有成员
- 关键字:friend
主要分为以下两种
① 友元函数
外部函数,声明成友元 → 能访问类的私有成员
② 友元类
另一个类,声明成友元 → 整个类都能访问私有成员
友元函数
在运算符重载那里说过。

在外面创造函数,在类里面声明。

然后接下来我们来看友元类


友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是C的友元。
有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤。
内部类
内部类是定义在另一个类内部的类,是两个独立的类,仅「作用域嵌套」,无继承 / 友元关系!
接下来我们来看一个内部类的代码

你觉得这个sizeof(A)应该是多少呢?

答案是4.
因为
静态成员变量,他的内存空间并不是储存在类里面,是存储在静态区。
那么A的私有成员变量只有一个int所以是四个字节,进而一步说明内部类是⼀个独⽴的类,跟定义在 全局相⽐,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
另外一点内部类默认是外部类的友元类。也就是说内部类可以访问外部类的私有,但是外部类不能访问内部类的私有。
内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考 虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其 他地⽅都⽤不了。
匿名对象
⽤ 类型(实参) 定义出来的对象叫做匿名对象,相⽐之前我们定义的 类型 对象名(实参) 定义出来的 叫有名对象

匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象
这样就对于一些场景来说会非常方便
我们先来看一个函数

库里面sort函数翻译过来大概是这个意思

我们要是只传两个值是默认从小到大排序,我们现在要实现从大到小排序


greater<int>() 是 STL 提供的降序比较函数对象,我们要在第三个参数传入greater的对象。但是有了这个匿名对象就很方便,而且匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可。



