一、初始化列表
1. 初始化列表定义
#include <iostream>
using namespace std;
class Date
{
public:
//初始化列表
Date(int year, int month, int day)
:_year(year),
_month(month),
_day(day)
{}
private:
int _year;
int _month;
int _day;
};
初始化列表的效果和构造函数差不多
2. 初始化列表适用范围
- 但初始化列表中成员变量只能出现一次
如果出现const修饰的成员变量,继续使用构造函数的话
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
_n = 10;
}
是会出现问题的,E0137说明cosnt修饰的变量已经被处理好了(已经初始化过了)
但如果使用列表初始化就没有问题
class Date
{
public:
//初始化列表
Date(int year, int month, int day, int n)
:_year(year),
_month(month),
_day(day),
_n(n)
{}
//初始化列表
Date(int year, int month, int day, int n)
:_year(year),
_month(month),
_day(day),
_n(n)
{}
初始化列表,这样也是可以的
//Date(int year, int month, int day, int n)
// :_n(n)
//{
// _year = year;
// _month = month;
// _day = day;
//}
private:
int _year;
int _month;
int _day;
const int _n;
};
以下三种情况初始化只能使用初始化列表
- 引用
- const修饰的变量
- 没有默认构造函数的自定义类 作成员变量时候
#include <iostream>
using namespace std;
class A
{
public:
A(int a)
{
_a = a;
}
private:
int _a;
};
class Date
{
public:
//初始化列表
Date(int year, int month, int day, int n, int i)
:_n(n),
_def(i),
_aa(1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
const int _n;
int& _def;
A _aa;
};
int main()
{
int i = 0;
Date d1(2022, 2, 19, 1, i);
return 0;
}
总结:初始化列表是对象的定义的地方
3. 推荐使用初始化列表初始化对象
假设用构造函数去初始化
#include <iostream>
using namespace std;
class A
{
public:
A(int a = 0)
{
cout << "A(int a = 0)" << endl;
_a = a;
}
A(const A& aa)
{
cout << "A(const A& aa)" << endl;
_a = aa._a;
}
A& operator=(const A& aa)
{
cout << "A& operator=(const A& aa)" << endl;
_a = aa._a;
return *this;
}
private:
int _a;
};
class Date
{
public:
//初始化列表
Date(int year, int month, int day, const A& aa)
{
_aa = aa;
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
A _aa;
};
int main()
{
A aa(10);
Date d1(2022, 2, 19, aa);
return 0;
}
在还没有执行构造函数里的初始化时候,编译器已经自动调用初始化列表了,(这里原理和构造函数一样,会去默认生成一个初始化列表调用)这样一看效率确实不是很高,所以建议使用初始化列表初始化对象
这里使用初始化列表
#include <iostream>
using namespace std;
class A
{
public:
A(int a = 0)
{
cout << "A(int a = 0)" << endl;
_a = a;
}
A(const A& aa)
{
cout << "A(const A& aa)" << endl;
_a = aa._a;
}
A& operator=(const A& aa)
{
cout << "A& operator=(const A& aa)" << endl;
_a = aa._a;
return *this;
}
private:
int _a;
};
class Date
{
public:
//初始化列表
Date(int year, int month, int day)
:_aa(1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
A _aa;
};
int main()
{
Date d1(2022, 2, 19);
return 0;
}
这次只调用了一次,默认构造函数,效率肯定比刚才高
4. 初始化列表按照声明顺序定义
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
二、explicit关键字
- 构造函数不仅可以构造与初始化对象,对于单个参数的构造函数,还具有类型转换的作用。
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year)
:_year(year)
{}
private:
int _year;
};
int main()
{
// 虽然他们两都是直接构造,但是过程是不一样的
Date d1(2022);
Date d2 = 2022; // 隐式类型转换
// 本来用2022构造一个临时对象Date(2022),再用这个对象拷贝构造d2
// 但是C++编译器在连续的一个过程中,多个构造会被优化,合二为一,但不在C++的规定里
// 所以这里被优化为直接就是一个构造
// 隐式类型转换 - 相近类型 -- 表示意义相似的类型
double d = 1.1;
int i = d;
const int& i = d;
// 强制类型转换 - 无关类型
int* p = &i;
int j = (int)p;
return 0;
}
如果要消除这样的隐士转换,在构造函数前加explicit即可