网站改版规则,wordpress全站网易云音乐播放,国外网站做营销,网站套站是什么意思目录 初始化列表特征疑惑区别必在初始化列表中初始化的三种成员变量1、引用成员变量程序例子#xff1a;运行结果#xff1a; 2、const成员变量程序例子#xff1a;运行结果#xff1a; 3、自定义类型成员#xff08;没有默认构造函数的类#xff09;程序例子#xff1a… 目录 初始化列表特征疑惑区别必在初始化列表中初始化的三种成员变量1、引用成员变量程序例子运行结果 2、const成员变量程序例子运行结果 3、自定义类型成员没有默认构造函数的类程序例子运行结果 总结 初始化列表
特征
① 以一个冒号开始 ② 以一个逗号作为数据成员的分割标志 ③ 在每个成员变量后面跟个括号括号内放入初始值或表达式
如下
class Date
{
public:// 构造函数Date(int year 0, int month 1, int day 1):_year(year), _month(month), _day(day) // 初始化列表{}private:int _year;int _month;int _day;
};注意 1、每个成员变量在初始化列表中只能出现一次初始化只能初始化一次
2、有3种成员变量必须放在初始化列表位置中进行初始化。哪3种变量在下面再做讲解。 疑惑
在学习之前有没有这么一个疑问前面学习 构造函数 的时候如下
class Date
{
public:// 构造函数Date(int year 0, int month 1, int day 1){_year year;_month month;_day day;}private:int _year;int _month;int _day;
};不是已经实现了对象变量的初始化了吗怎么又蹦出来个初始化列表一说跟前面学的构造函数的初始化有什么区别吗
答案是两者是有所区别的。 首先什么叫初始化或者说初始化的文意是什么初始化的含义是只运行一遍、只赋值一次…即一次以后就没有初始化的什么事了。但是在前面学习的构造函数中如下 // 构造函数Date(int year 0, int month 1, int day 1){_year year;_month month;_day day;_year 2023;_month 12;_day 13;}我们可以在构造函数里面对要初始化的变量多次赋值而这明显在概念上与初始化相违背。 确切的说这种方式构造函数内的语句只能称为赋初值但不能称为初始化。因为初始化只能初始化 ( 赋值 ) 一次而这里的构造函数体内可以多次赋值。这便是两者在概念上的差别。 区别
前面了解了两种 “初始化” 概念上的的区别后那么有个问题在功能上、实际效果中两者有没有什么区别呢
我们发现不管是使用初始化列表 // 构造函数Date(int year 0, int month 1, int day 1):_year(year), _month(month), _day(day) // 初始化列表{}进行初始化还是使用 // 构造函数Date(int year 0, int month 1, int day 1){_year year;_month month;_day day;}这种方式实现“初始化”这里两者在功能实现上并没有什么区别啊。 是的对于有些类型的成员变量不管是放在初始化列表中进行初始化还是放在构造函数体内进行初始化两者在实际效果上并没有什么区别。 但是 ! 如前面特征中注意事项中的第二点提到的有3种类型的成员变量必须放在初始化列表中进行初始化。
下面对这3种类型依个进行学习。 必在初始化列表中初始化的三种成员变量 在类中成员变量为以下三种类型之一时初始化必须要在初始化列表中才能够让编译器自动去初始化变量。 1、引用成员变量
当类中的成员变量类型为引用时如下
程序例子
class A
{
public:// 可以理解成初始化列表是对象的成员变量的定义的地方A(int ref 0):_ref(ref){}void print(){cout _ref endl ;}
private:// 成员变量的声明int _ref; // 引用};int main()
{A a1;a1.print();return 0;
} 运行结果 收入眼帘的我们发现在初始化列表中初始化过的引用成员变量_ref 并不是我们所想的初始化为0啊。这是因为 引用成员变量_ref在初始化列表中引用的是构造函数中的形参形参缺省值是0当程序运行到初始化列表中_ref的值确实是被初始化为0的我们通过调试可以发现结果如下 但是出了构造函数后形参ref在构造函数作用域内时开辟的空间将随着出了构造函数的作用域而被销毁空间使用权归还编译器这时 _ref 引用的空间依旧是原先形参开辟的空间但出了构造函数后那块空间已经归还编译器了因此那块空间将是随机值所以打印的结果是随机值。
要想如我们所构想的给引用成员变量_ref 初始化为0那么我们要先给 _ref 提供一个有效的对象。这个对象并不会在出了构造函数后就被销毁如
class A
{
public:static int s;A():_ref(s){}void print(){cout _ref endl;}
private:// 成员变量的声明int _ref; // 引用};int A::s 0;
int main()
{A a1;a1.print();A::s 10;a1.print();return 0;
}
我们在类中声明了一个静态整形变量s然后将_ref 初始化为静态整形变量s的别名同时在类外对s定义初始化为0。这时就完成了对 引用成员变量 _ref 的初始化即绑定到静态对象s的地址上。
运行结果 结果显示当我们对静态整形变量s更改时引用成员变量 _ref 的值也跟着更改。
那么如果我们将引用成员变量 _ref 放在构造函数体内进行初始化呢 static int s;A(){_ref s;}编译器会报错编译不通过。具体是为什么我是这么理解的 首先引用的特性是在创建时必须被初始化。在private中只是引用声明还没有被创建而当用类创建出对象时编译器自动调用构造函数对成员变量初始化。 其次程序是先运行到初始化列表而后才进入函数体内的。当运行到函数体内时引用成员变量已经被创建出来了而被创建出来后又没有对其初始化即给定一个对象在C中对象和变量是等效的说法。这违背了引用的特性所以编译器会报错。 所以要在引用成员变量被创建之前即在初始化列表中对引用成员变量进行初始化。 可以这么理解在初始化列表中 _ref(s) 时引用成员变量_ref被创建出来并且作为了静态对象s的引用。而程序运行如果出了初始化列表进入函数体内时已经完成了对引用成员变量 _ref 的创建了。 以上便是必须在初始化列表中初始化的成员变量类型之一引用成员变量。接下来对下一个成员变量类型进行学习。 2、const成员变量
程序例子
class B
{
public:// 可以理解成初始化列表是对象的成员变量的定义的地方B(int a 0):_n(a){}void print(){cout _n endl; }
private:// 成员变量的声明const int _n; // const
};int main()
{B b;return 0;
}运行结果 原因和引用成员变量类似也是因为const关键字的特性 const 变量在声明时必须被初始化因为它们一旦被赋值之后就不能再修改。 所以当程序跑到构造函数体内时const修饰的成员变量 _n 已经被创建出来了但是又没有初始化所以编译器会报错。 因此同样的对于const修饰的成员变量的初始化必须在初始化列表中。
下面学习最后一种必须在初始化列表中初始化的成员变量类型。 3、自定义类型成员没有默认构造函数的类
对于自定义类型成员并且自定义类型是没有默认构造函数的类就是只有需要传参才能对对象进行初始化的构造函数。如下
程序例子
class A
{
public:A(int a):_a(a){}int Getnum(){return _a;}
private:int _a;
};class B
{
public:// 可以理解成初始化列表是对象的成员变量的定义的地方B(int a, int ref):_aobj(1){}void print(){cout _aobj.Getnum() endl;}
private:// 成员变量的声明A _aobj; // 没有默认构造函数需要传参才可以调用的构造函数
};int main()
{B b;b.print();return 0;
}程序中类B中的有一个成员变量_aobj类型是自定义类型类A。在类A中有一个显示构造函数但是因为该构造函数参数没有缺省值因此要想调用这个构造函数时必须得传参数。即类A中是一个没有默认构造函数不用传参数就能调用的构造函数 的类。 这种情况下要想对类B中的成员变量_aobj 初始化时必须得放到初始化列表中进行i初始化。先看看程序运行的结果 运行结果 结果如期所至对象_aobj 的成员变量被初始化成1。 至于为什么没有默认构造函数的自定义类型的变量初始化时必须放在初始化列表中按照我的理解和前面的原因是一致的 因为程序跑到类B中构造函数体内时成员变量类A实例出的对象 _aobj 已经被创建出来了而创建出来时又没有给对象_aobj传入参数又因为类A中没有默认构造函数所以对象_aobj调用构造函数失败即类B的成员变量_aobj初始化失败导致类B实例的对象b初始化失败因此编译器报错。 如果还有疑问命名函数体内已经 _aobj(1); 传参数初始化了啊为什么说没给对象_aobj传参呢 对于这个疑问可以理解为当程序运行到构造函数体内的 _aobj(1); 时 当_aobj初始化是放在构造函数体内而不是初始化列表时 对象_aobj已经在初始化列表处的位置被创建出来了 创建出来的时候又因为没有传入参数而对象_aobj的类型A中又没有默认构造函数所以对于对象_aobj调用类A的构造函数进行初始化时编译出错。 至此对于必须放在初始化列表中进行初始化的类型介绍完毕。 如果到此还不太理解可以联想以上三种类型在普通函数或者main函数中定义创建时的要求是什么可以理解为是一样的概念。如下
class C
{
public:C(int c): _c(c){}
private:int _c;
}int main()
{const int a; // 编译能通过吗int b; // 编译能通过吗class C; // 编译能通过吗return 0;
}以上在main函数中创建出来的三个变量a、b、c 能被编译器顺利通过吗这个问题就留给你们自己去思考了。 总结
1、对于大多数类型的成员变量初始化时放在初始化列表也行放在构造函数体内也罢效果都是一样的
2、对于以上特殊的三种成员变量类型必须放在初始化列表中进行初始化
注意事项 成员变量的初始化顺序就是成员变量在类中声明的顺序跟在初始化列表中的先后次序无关 建议
1、对于所有的成员变量的初始化都放在初始化列表中进行初始化。因为 C 的设计中构造函数的初始化列表就是专门用于初始化成员变量的地方。将所有成员变量的初始化都放在初始化列表中有助于提高代码的清晰度和一致性。
2、初始化列表中成员变量初始化的先后次序与类中成员变量的声明顺序保持一致有助于代码的可观读性。