目录

一.初始化列表

1.列表格式:

情况1: 成员变量中有const成员—— 但列表处成员不被初始化时

情况1: 成员变量中有const成员—— 给缺省值时:

 情况1: 成员变量中有const成员—— 列表处成员不仅初始化,还有缺省值

情况2:成员是自定义类型成员时:

优化方案1:使得类A有默认构造

 优化方案2:采用初始化列表

总结:


一.初始化列表


        虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。采用从初始化列表的缘故是因为成员变量中会存在一些特殊情况,只能由初始化列表去赋值。

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.尽量提供默认构造函数(推荐:全缺省)。

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐