行业知识
C++奇迹之旅:初始化列表与explicit
2026-11-09 19:40:43
来源:互联网转载
文章目录
- 再谈构造函数
- 构造函数体赋值
- 初始化列表
- 初始化列表效率
- 隐式类型转换
- 复制初始化
- 单多参数构造函数
- explicit关键字
- 总结
再谈构造函数
构造函数体赋值
在创建对象时,编译器通过调用构造函数,给对象中各个变量一个合适的初始值
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){//}private:int _year;int _month;int _day;};为什么要有初始化列表来赋初值,不能直接给缺省值,或者传参吗?
class Date{public:Date(int year, int month, int day){_year = year;_month = month;_day = day;_x = 1;}private:int _year;int _month;int _day;//必须在定义初始化const int _x;};由于const必须在定义时就要进行初始化,而这个在构造函数中_x=1的行为是赋值行为,不是初始化,因此const 修饰_x无法再赋值。引用&也是如此,需要在定义的时候并且进行初始化,不能分开。
因此对于普通的内置类型,普通成员变量都可以在函数体或者在初始化列表进行初始化,
int _year;int _month;int _day;
因为在这里只是声明,没有定义,定义时实例化的时候完成的,而有些特殊的成员变量需要再定义的时候就初始化,而不是再通过赋值。
class Date{public: Date(int year, int month, int day, int& refDay) : ref(refDay) , _x(1) { _year = year; _month = month; _day = day; } void printDate() { std::cout int someDay = 22; Date date1(2024, 4, 22, someDay); date1.printDate(); return 0;}public:A(int a):_a(a){}private:int _a;};class B{public:B(int a, int ref):_aobj(a), _ref(ref), _n(10){};private:A _aobj; // 没有默认构造函数int& _ref; //引用const int _n;//const};int main(){int x = 10;B bb(20, x);return 0;}public: Date(int year, int month, int day, int& refDay) : ref(refDay) , _x(1) { _year = year; _month = month; _day = day; }private: int _year; int _month; int _day; int& ref; const int _x;};public:Stack(size_t capacity = 4){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}private:DataType* _array;int _capacity;int _size;};class MyQueue{public:// 初始化列表,不管你写不写,每个成员变量都会先走一遍// 自定义类型的成员会调用默认构造(没有默认构造就编译报错)// 内置类型有缺省值用缺省值,没有的话,不确定,要看编译器,有的编译器会处理,有的不会处理// 先走初始化列表 + 再走函数体// 实践中:尽可能使用初始化列表初始化,不方便再使用函数体初始化MyQueue():_size(1), _ptr((int*)malloc(40)){memset(_ptr, 0, 40);}private:// 声明Stack _pushst;Stack _popst;// 缺省值 给初始化列表用的int _size = 0;const int _x = 10;int* _ptr;};int main(){MyQueue q;return 0;}public:A(int a):_a1(a),_a2(_a1){}void Print(){cout A aa(1);aa.Print();}A. 输出1 1B.程序崩溃C.编译不通过D.输出1 随机值public:A(int a):_a1(a),_a2(_a1){}void Print(){cout A aa(1);aa.Print();}//...A(); //默认构造函数A(const A& d); //拷贝构造函数A& operator=(const A& d); //赋值函数};class B{public:B(const A& a); //B的成员对象private:A m_a; //成员对象};...}m_a = a;...}public:A(int a):_a(a){cout A aa1(1);//拷贝构造A aa2 = aa1;A aa3 = 3;return 0;}public:A(int a):_a(a){cout cout A aa1(1);A aa3 = 3;return 0;}public:A(int a):_a(a){cout A& raa = 3;return 0;}public:void Push(const A& aa){//...}//...};int main(){Stack st;A a1(1);st.Push(a1);A a2(2);st.Push(a2);//可以直接写st.Push(2);st.Push(4);return 0;}list}A aaa1(1, 2);A aaa2 = { 1, 2 };const A& aaa3 = { 1, 2 };public:// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用// explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译explicit Date(int year):_year(year){}/*// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转 换作用// explicit修饰构造函数,禁止类型转换explicit Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}*/Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}private:int _year;int _month;int _day;};void Test(){Date d1(2022);// 用一个整形变量给日期类型对象赋值// 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值d1 = 2023;// 将1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转换的作用}