C++基础——初始化列表
目录
情况1: 成员变量中有const成员—— 但列表处成员不被初始化时
情况1: 成员变量中有const成员—— 列表处成员不仅初始化,还有缺省值
一.初始化列表
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。采用从初始化列表的缘故是因为成员变量中会存在一些特殊情况,只能由初始化列表去赋值。
1.列表格式:
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟
一个放在括号中的初始值或表达式。
自定义类型 类型名( )
: 成员变量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变量时,往往需要定义时就要给初始化值:
而构造函数并不能满足需求,需要用到初始化列表。
情况1: 成员变量中有const成员—— 但列表处成员不被初始化时
class B{
public:
B()
:_n(10) //初始化列表
{}
private:
const int _n; // const
int _m;
};
总结:每个成员都要走初始化列表,就是成员没有在初始化列表写,也会走。但初始化列表只有_n被初始化,而_m没有,所以赋给随机值
情况1: 成员变量中有const成员—— 给缺省值时:
class B
{
public:
B()
:_n(10) //初始化列表
{}
private:
const int _n; // const
int _m=1; //缺省值
};
int main() {
B b2;
}
总结:因为成员变量处给了缺省值,但此时_m还处于随机值,直到运行时编译器进入构造函数初始化列表,_m才会使用缺省值
情况1: 成员变量中有const成员—— 列表处成员不仅初始化,还有缺省值
class B
{
public:
B(int a, int ref)
:_n(10) //初始化列表
, _m(3)
{}
private:
const int _n; // const
int _m=100; //缺省值
};
总结:这次,_m不仅有缺省值,还在初始化列表中被初始化,而编译器会优先选择初始化列表的值初始化列表使用权>缺省值使用权限,所以最终_m最终是按照初始化列表的赋值使用,其值为3。
情况2:成员是自定义类型成员时:
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赋值,所以编译器会报错。
优化方案1:使得类A有默认构造函数(这个默认构造可以是全缺省的构造函数,也可以是无参构造,或者直接不写由编译器自动生成的构造函数)
编译器运行正常,自定义类型成员变量_aa赋值为10,const成员变量_n初始化值为10。
优化方案2:让类A放弃默认构造,采用初始化列表
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.尽量提供默认构造函数(推荐:全缺省)。
更多推荐
所有评论(0)