目录
一 引入
二 初始化列表概念
三 初始化列表特性
1 引用和const
2 混合使用
3 自定义成员情况
四 初始化列表中的初始化顺序
五 总结
一 引入
构造函数体赋值
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量 的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始 化一次,而构造函数体内可以多次赋值
二 初始化列表概念
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟 一个放在括号中的初始值或表达式。
class Date
{
public://Date(int year, int month, int day)//{// // 函数体内初始化// _year = year;// _month = month;// _day = day;//}Date(int year, int month, int day):_year(year), _month(month), _day(day){// 初始化列表}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}private:// 声明int _year; int _month;int _day;
};
上面的例子中两个构造函数的结果是一样的。上面的构造函数(使用初始化列表的构造函数)显式的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化。
初始化和赋值对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。对非内置类型成员变量,为了避免两次构造,推荐使用类构造函数初始化列表。但有的时候必须用带有初始化列表的构造函数
三 初始化列表特性
1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
自定义类型成员(且该类没有默认构造函数时)
总之 这三类不能在函数内部初始化
1 引用和const
class Date{public:Date(int year, int month, int day){// 函数体内初始化_year = year;_month = month;_day = day;int& _ref;//引用 -->errorconst int _n;//const -->error}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}private:// 声明int _year; // 缺省值int _month;int _day;int& _ref;//引用const int _n;//const};
这是为什么?
引用和const只能初始化而不是赋值
之前有讲过,若是我们自己不去实现构造函数的话,类中会默认提供一个构造函数来初始化成员变量,对于【内置类型】的变量不会处理,对【自定义类型】的变量会去调用它的构造函数。那么对于这里的_year, _month, _day, _ref, _n都属于内置类型的数据,所以编译器不会理睬,可是引用变量, const
修饰的变量又必须要初始化,所以初始化列表诞生
有人说给个缺省值不就好了 这个办法确实是可以解决我们现在的问题,因为C++11里面为内置类型不初始化打了一个补丁,在声明的位置给到一个初始化值,就可以很好地防止编译器不处理的问题
但是现在我想问一个问题:如果不使用这个办法呢?你有其他方法吗?难道C++11以前就那它没办法了吗?
改正如下:
class Date
{
public://Date(int year, int month, int day)//{// // 函数体内初始化// _year = year;// _month = month;// _day = day;// int& _ref;//引用 -->error// const int _n;//const -->error//}Date(int year, int month, int day):_year(year), _month(month), _day(day), _ref(year), _n(1){// 初始化列表}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}private:// 声明int _year; // 缺省值int _month;int _day;int& _ref;//引用const int _n;//const
};
2 混合使用
当然函数体内初始化和初始化列表可以混着用
Date(int year, int month, int day): _ref(year), _n(1){// 函数体内初始化_year = year;_month = month;_day = day;}
3 自定义成员情况
class A
{
public:A(int a = 0)//默认构造:_a(a){cout << "A(int a = 0)" << endl;}
private:int _a;
};class Date
{
public:Date(int year, int month, int day){// 函数体内初始化_year = year;_month = month;_day = day;}private:// 声明int _year; int _month;int _day;A _aa;//调用默认构造
};int main()
{Date d(2023, 11, 14);return 0;
}
1 如果A没有默认构造呢? -->初始化列表
class A
{
public:A(int a)//这不是默认构造:_a(a){cout << "A(int a = 0)" << endl;}
private:int _a;
};class Date
{
public:Date(int year, int month, int day):_aa(10){// 函数体内初始化_year = year;_month = month;_day = day;}private:// 声明int _year; int _month;int _day;A _aa;//没有默认构造
};int main()
{Date d(2023, 11, 14);return 0;
}
2 如果不想使用A的默认构造的值呢?-->初始化列表
class A
{
public:A(int a = 10)//默认构造:_a(a){cout << "A(int a = 0)" << endl;}
private:int _a;
};class Date
{
public:Date(int year, int month, int day):_aa(1000){// 函数体内初始化_year = year;_month = month;_day = day;}private:// 声明int _year; int _month;int _day;A _aa;//调用默认构造
};int main()
{Date d(2023, 11, 14);return 0;
}
尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化
四 初始化列表中的初始化顺序
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};int main() {A aa(1);aa.Print();
}
先走的_a2, 此时_a1还没有初始化, 给的随机值,.
五 总结
初始化列表解决的问题:
1、必须在定义的地方显示初始化 引用 const 没有默认构造自定义成员
2、有些自定义成员想要显示初始化,自己控制
尽量使用初始化列表初始化
构造函数能不能只要初始化列表,不要函数体初始化?
不能,因为有些初始化或者检查的工作,初始化列表也不能全部搞定
80-100%初始化列表搞定,还有需要用函数体,他们可以混着用
我们来看看下面这段代码
class Stack
{
public:Stack(int n = 2):_a((int*)malloc(sizeof(int)* n)), _top(0), _capacity(n){//...//cout << "Stack(int n = 2)" << endl;if (_a == nullptr){perror("malloc fail");exit(-1);}memset(_a, 0, sizeof(int) * n);}//...int* _a;int _top;int _capacity;
};class MyQueue
{
public:MyQueue(int n1 = 10, int n2 = 20)//形成默认构造函数:_s1(n1), _s2(n2){}private:Stack _s1;Stack _s2;int _size = 0;
};int main()
{MyQueue q1;MyQueue q2(100, 100);//有初始化列表那就先走初始化列表return 0;
}
这样我们就能控制我们怎样控制我们的默认构造.
这节是对上一节类和对象(2)构造函数的续集, 建议两个章节要有先后理解, 本节内容比较简单, 注重理解 上一节的博客【C++】类和对象(2)--构造函数-CSDN博客
析构函数 【C++】类和对象(4)--析构函数-CSDN博客
继续加油!