文章目录
- 一、语言的向前兼容
- 二、默认成员函数
- 三、构造函数
- 3.1 概念
- 3.2 自己定义的构造函数
- 3.2.1 有参和无参的构造函数
- 3.2.1 有缺省参数的构造函数
- 3.3 默认构造函数
- 3.3.1 几种默认构造函数
- 3.3.2 默认构造函数的特点
- 3.4 编译器生成的默认构造函数
- 3.4.1 函数初始化规则
- 四、构造函数的初始化列表
- 4.1 初始化格式
- 4.1.1全在函数体内初始化
- 4.1.2 全部用初始化列表
- 4.1.3 初始化列表和函数内初始两者混合用
- 4.2 对象的声明和定义
- 4.2.1 成员变量的定义
- 4.2.2 初始化顺序
- 4.3 函数体内
一、语言的向前兼容
已经设计好的规则不能修改,只能优化、弥补。
因为可能别人已经使用这个规则了,一旦改动了规则,那之前的规则就不能用了,就会出很多错误,所以只能优化,以便以前的规则也能用。🌷
二、默认成员函数
一个自定义的类,如果我们没有写任何成员函数,那编译器会自动生成6个默认成员函数。
既然是默认生成的,那如果自己写了,对应的默认成员函数就不会生成了。
三、构造函数
3.1 概念
构造函数是用来初始化对象的,而不是用来开空间创建对象的。🌵
有以下特征:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。🍊
- 构造函数可以重载。
3.2 自己定义的构造函数
3.2.1 有参和无参的构造函数
- 有参和无参的构造函数如下:
class Date
{
public:
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
从上述可见,构造函数可以重载。🥝
- 有参和无参构造函数的调用:
正确调用如下:
Date d1;
Date d2(2024,1,1);
无参函数的错误调用如下:
Date d1(); // 错误
无参调用不可以带括号,因为这样的话,就无法与函数声明分开了,这个调用可以看作名为 d1
的函数的声明,返回值是 Date
。🧩
3.2.1 有缺省参数的构造函数
- 有缺省参数的构造函数如下:
Date(int year = 1, int month = 1, int day = 1) //全缺省
{
_year = year;
_month = month;
_day = day;
}
Date(int year, int month = 1,int day =1 ) //半缺省
{
_year = year;
_month = month;
_day = day;
}
- 有缺省参数的构造函数的调用如下:
Date d3(2024,1,1);
Date d4(2024,1);
Date d5(2025);
- 全缺省,无参调用的冲突:
Date d;
全缺省和无参的构造函数都可以这样调用,所以如果我们只是写了这两个函数中的一个,那就可以这样调用,但如果两个都写了,这样调用就会有歧义,因为两个都可以这样调用,所以为了避免歧义,两个函数不能同时存在。一般保留有缺省参数的构造函数,既能有参构造,又能无参构造。⭐
3.3 默认构造函数
3.3.1 几种默认构造函数
可以叫做默认构造函数的,有以下几种构造函数:🍓
- 我们不写默认构造函数时,编译器默认生成的那个构造函数,叫做默认构造函数。
- 无参构造函数也可以叫做默认构造函数。
- 全缺省的构造函数也可以叫做默认构造函数。
总结:可以不传参数就调用的构造函数,都可以叫默认构造。🍇
3.3.2 默认构造函数的特点
- 上述三种默认构造函数不能同时存在,只能存在一个。
- 自己写了构造函数,编译器就不会自动生成构造函数了,无论写的是哪种构造函数。🌲
3.4 编译器生成的默认构造函数
3.4.1 函数初始化规则
- 内置类型和自定义类型:
int
,float
,doubld
, 指针等都是内置类型。🍏
class
,struct
声明的都是自定义类型。 - 初始化规则:
默认生成的构造函数对成员变量中的内置类型不做处理,对自定义类型会去调用他的默认构造函数。🍅
- 有些编译器会处理内置类型,建议当成不处理。
VS2019
优化后,又有内置类型,又有自定义类型就都处理一下,只有内置类型就不处理。🥕
但如果我们不写构造函数,使用默认生成的构造函数,那默认构造函数对内置类型的成员变量不做处理的话只能是随机值。如果我们既不想成员变量是个随机值,又不想专门写一个构造函数的话,就可以使用成员变量的缺省值,如下所示。🍈
class Date
{
public:
private:
int _year = 1;
int _month = 1;
int _day = 1;
};
内置类型的成员变量可以有缺省值,算是一种补充。
- 书写规则:
- 一般情况下,我们自己写构造函数。
- 成员都是自定义类型,或者声明时给了缺省值的可以考虑使用编译器自己生成构造函数。🧊
四、构造函数的初始化列表
4.1 初始化格式
4.1.1全在函数体内初始化
class Date
{
public:
// 函数体内初始化
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year = 1;
int _month = 1;
int _day = 1;
};
属于成员变量定义后再赋值的。🌳
4.1.2 全部用初始化列表
class Date
{
public:
//初始化列表初始化
Date(int year, int month, int day)
:_year(year),
_month(month),
_day(day)
{}
private:
int _year = 1;
int _month = 1;
int _day = 1;
};
属于成员变量定义时初始化的。🍎
4.1.3 初始化列表和函数内初始两者混合用
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
, _month(month)
{
_day = day;
}
private:
int _year = 1;
int _month = 1;
int _day = 1;
};
_year
和 _month
使用初始化列表初始化,_day
在函数内初始化。 🥕
4.2 对象的声明和定义
4.2.1 成员变量的定义
每个成员变量都是在初始化列表中定义的,即使有些成员变量没有在初始化列表中初始化,这些变量仍然是在此处定义的,只不过我们没有在这里初始化,所以它隐藏起来了而已。🍉
在进入 {
之前,就已经完成了成员变量的定义。
- 成员变量的初始值:
- 定义只能定义一次
Date(int y,int m, int d)
:_year(0)
,_month(m)
,_year(y) //error
{}
前面写了 _year
,那时就已经定义了变量并初始化了,此时再写相当于再次定义一个叫_year
的变量 —— 错误。🍋
4.2.2 初始化顺序
初始化列表初始化是根据声明顺序初始化的,与其在初始化列表中的顺序无关。
class A
{
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
private:
int _a2;
int _a1;
};
上面代码的逻辑是:先用 _a1
的值初始化 _a2
,再用 a
初始化 _a1
。🍒
4.3 函数体内
-
函数体内初始化:
通过初始化列表定义并初始化数据后,可能有些变量初始值是随机值,可以在函数体内再赋值。
但是推荐使用初始化列表,因为不管怎样都会走初始化列表,初始化列表处就是变量定义处,定义处初始化了,后面就不用赋值了。🫐 -
函数体内进行其他工作:
有些初始化或者检查的工作,初始化列表不能搞定,还需要在函数体内完成。🥝
本文到这里就结束了,如果对您有帮助,希望得到一个赞!🌷
如有错漏,欢迎指正!😄