目录
情况1: 成员变量中有const成员—— 但列表处成员不被初始化时
情况1: 成员变量中有const成员—— 列表处成员不仅初始化,还有缺省值
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。采用从初始化列表的缘故是因为成员变量中会存在一些特殊情况,只能由初始化列表去赋值。
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟
一个放在括号中的初始值或表达式。
自定义类型 类型名( )
: 成员变量1 (赋值)
, 成员变量2 (赋值)
, 成员变量3 (赋值)
......
{
//构造函数内部......
}
【注意】:
1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
例,代码如下:
- class Date
- {
- public:
- //构造函数
- Date(int year, int month, int day)
- //初始化列表
- :_year(1999)
- ,_month(10)
- ,_day(30)
- {
- _year = year;
- _month = month;
- _day = day;
- }
-
- void Print()
- {
- cout << "Print()" << endl;
- cout << "year:" << _year << endl;
- cout << "month:" << _month << endl;
- cout << "day:" << _day << endl << endl;
- }
-
- private:
- int _year; // 年
- int _month; // 月
- int _day; // 日
- };
-
- int main(){
- Date d1;
- }
当创建对象d1时,调用该类构造函数,在进入构造函数前,各成员变量都是随机值,编译器会先进入初始化列表,为各成员变量初始化值。
例2:Stack类初始化列表:
- typedef int DataType;
- class Stack
- {
- public:
- Stack(size_t capa)
- //初始化列表
- :_size(0)
- , _capacity(capa)
- , _array((DataType*)malloc(capa * sizeof(DataType)))
- {
- //构造函数内部
- if (nullptr == _array)
- {
- perror("malloc申请空间失败");
- return;
- }
- }
- void Push(const DataType& data)
- {
- // CheckCapacity();
- _array[_size] = data;
- _size++;
- }
- ~Stack()
- {
- if (_array)
- {
- free(_array);
- _array = nullptr;
- _capacity = 0;
- _size = 0;
- }
- }
- private:
- DataType* _array;
- size_t _size;
- size_t _capacity;
- };
也可以将构造函数和初始化列表混合着用:
- typedef int DataType;
- class Stack
- {
- public:
- Stack(size_t capa)
- :_size(0)
- , _capacity(capa)
- {
- //构造函数内部
- _array =(DataType*)malloc(capa * sizeof(DataType));
- if (nullptr == _array)
- {
- perror("malloc申请空间失败");
- return;
- }
- }
在初始化列表中先初始化size和capacity,针对于指针开辟空间的成员还是放在构造函数内部好,因为要与判断相连接,还是混着来比较好。
初始化列表最重要的作用就是为特殊的成员变量提供初始化帮助:
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
自定义类型成员(且该类没有默认构造函数时)
当我们在定义const变量时,往往需要定义时就要给初始化值:
而构造函数并不能满足需求,需要用到初始化列表。
- class B{
- public:
- B()
- :_n(10) //初始化列表
- {}
- private:
- const int _n; // const
- int _m;
- };
总结:每个成员都要走初始化列表,就是成员没有在初始化列表写,也会走。但初始化列表只有_n被初始化,而_m没有,所以赋给随机值
- class B
- {
- public:
- B()
- :_n(10) //初始化列表
- {}
- private:
- const int _n; // const
- int _m=1; //缺省值
- };
- int main() {
- B b2;
- }
总结:因为成员变量处给了缺省值,但此时_m还处于随机值,直到运行时编译器进入构造函数初始化列表,_m才会使用缺省值
- class B
- {
- public:
- B(int a, int ref)
- :_n(10) //初始化列表
- , _m(3)
- {}
- private:
- const int _n; // const
- int _m=100; //缺省值
- };
总结:这次,_m不仅有缺省值,还在初始化列表中被初始化,而编译器会优先选择初始化列表的值初始化列表使用权>缺省值使用权限,所以最终_m最终是按照初始化列表的赋值使用,其值为3。
- class A{
- public:
- //构造函数,并不是默认构造
- A(int a)
- :_a(a)
- {}
- private:
- int _a;
- };
-
- class B{
- public:
- B()
- :_n(10) //初始化列表
- {}
- private:
- const int _n; // const
- A _aa;
- };
-
- int main() {
- B b4;
- }
总结:编译器执行对象b4的创建时,会进入类B的构造函数,因为成员变量有自定义类型 _aa,所以编译器会进入类A中找它的默认构造,但类A中没有默认构造,类A中只有自己写的构造函数,此时也就无法给自定义类型成员_aa赋值,所以编译器会报错。
编译器运行正常,自定义类型成员变量_aa赋值为10,const成员变量_n初始化值为10。
- class A{
- public:
- //不采用默认构造,但初始化列表给值了也不会报错
- A(int a )
- :_a(a)
- {}
- private:
- int _a;
- };
-
- class B{
- public:
- B()
- :_n(10) //初始化列表
- ,_aa(75)
- {}
- private:
- const int _n; // const
- A _aa;
- };
- int main() {
- B b;
- return 0;
- }
调用b的成员时,即调用A _aa时会到A类中调用它的默认构造,没有默认构造时,使用初始化列表,也不会报错。
对于引用成员来说,它与const一样,都是需要在定义时就得初始化,而采用构造函数并不能满足需求,只能使用初始化列表。
1. 对于内置类型,没有成员在初始化列表中显示时,有缺省值就使用缺省值,没有缺省值就只能是随机值。
2.对于自定义类型,调用它的默认构造函数,若没有默认构造函数,则报错!!!
3.默认构造三种形式:无参构造、全缺省构造、不亲自写编译器自己生成的默认构造。4.要尽量使用初始化列表去初始化成员变量(因为有const、自定义类型、引用)。
5.尽量提供默认构造函数(推荐:全缺省)。