当前位置:首页 > > 充电吧
[导读]在我们实际编程中,我们经常会碰到变量初始化的问题,对于不同的变量初始化的手段多种多样,比如说对于一个数组我们可以使用 int arr[] = {1,2,3}的方式初始化,又比如对于一个简单的结构体:[

在我们实际编程中,我们经常会碰到变量初始化的问题,对于不同的变量初始化的手段多种多样,比如说对于一个数组我们可以使用 int arr[] = {1,2,3}的方式初始化,又比如对于一个简单的结构体:


[cpp]view plaincopy structA { intx; inty; }a={1,2};

这些不同的初始化方法都有各自的适用范围和作用,且对于类来说不能用这种初始化的方法,最主要的是没有一种可以通用的初始化方法适用所有的场景,因此C++11中为了统一初始化方式,提出了列表初始化(list-initialization)的概念。

统一的初始化方法

在C++98/03中我们只能对普通数组和POD(plain old data,简单来说就是可以用memcpy复制的对象)类型可以使用列表初始化,如下:


数组的初始化列表:


 int arr[3] = {1,2,3}


POD类型的初始化列表:

[cpp]view plaincopy structA { intx; inty; }a={1,2}; 在C++11中初始化列表被适用性被放大,可以作用于任何类型对象的初始化。如下:



[cpp]view plaincopy classFoo { public: Foo(int){} private: Foo(constFoo&); }; int_tmain(intargc,_TCHAR*argv[]) { Fooa1(123);//调用Foo(int)构造函数初始化 Fooa2=123;//errorFoo的拷贝构造函数声明为私有的,该处的初始化方式是隐式调用Foo(int)构造函数生成一个临时的匿名对象,再调用拷贝构造函数完成初始化 Fooa3={123};//列表初始化 Fooa4{123};//列表初始化 inta5={3}; inta6{3}; return0; } 由上面的示例代码可以看出,在C++11中,列表初始化不仅能完成对普通类型的初始化,还能完成对类的列表初始化,需要注意的是a3,a4都是列表初始化,私有的拷贝并不影响它,仅调用类的构造函数而不需要拷贝构造函数,a4,a6的写法是C++98/03所不具备的(可以不写等号),是C++11新增的写法。


同时列表初始化方法也适用于用new操作等圆括号进行初始化的地方,如下:


[cpp]view plaincopy int*a=newint{3}; doubleb=double{12.12}; int*arr=newint[]{1,2,3}; 让人惊奇的是在C++11中可以使用列表初始化方法对堆中分配的数组进行初始化,而在C++98/03中是不能这样做的。

列表初始化的一些细节

虽然列表初始化提供了统一的初始化方法,但是同时也会带来一些使用上的疑惑需要各位苦逼码农注意,比如对下面的自定义类型的例子:



[cpp]view plaincopy structA { intx; inty; }a={123,321}; //a.x=123a.y=321 structB { intx; inty; B(int,int):x(0),y(0){} }b={123,321}; //b.x=0b.y=0 对于自定义的结构体A来说也是普通的POD类型,使用列表初始化并不会引起问题,x,y都被正确的初始化了,但看下结构体B和结构体A的区别在于结构体B定义了一个构造函数,并使用了成员初始化列表来初始化B的两个变量,因此列表初始化在这里就不起作用了,B采用的是构造函数的方式来完成变量的初始化工作。


那么如何区分一个类(class struct union)是否可以使用列表初始化来完成初始化工作呢?关键问题看这个类是否是一个聚合体(aggregate),首先看下C++中关于类是否是一个聚合体的定义:

(1)无用户自定义构造函数。

(2)无私有或者受保护的非静态数据成员

(3)无基类

(4)无虚函数

(5)无{}和=直接初始化的非静态数据成员。下面我们逐个对上述进行分析。

1、首先存在用户自定义的构造函数的情况,示例如下:


[cpp]view plaincopy structFoo { intx; inty; Foo(int,int){cout<<"Fooconstruction";} }; int_tmain(intargc,_TCHAR*argv[]) { Foofoo{123,321}; cout<<foo.x<<""<<foo.y; return0; } 输出结果为:Fooconstruction -858993460 -858993460


可以看出对于有用户自定义构造函数的类使用初始化列表其成员初始化后变量值是一个随机值,因此用户必须以用户自定义构造函数来构造对象。

2、类包含有私有的或者受保护的非静态数据成员的情况

[cpp]view plaincopy structFoo { intx; inty; //Foo(int,int,double){} protected: doublez; }; int_tmain(intargc,_TCHAR*argv[]) { Foofoo{123,456,789.0}; cout<<foo.x<<""<<foo.y; return0; } 实例中z是一个受保护的成员变量,该程序直接在VS2013下编译出错,error C2440: 'initializing' : cannot convert from 'initializer-list' to 'Foo',而如果将z变量声明为static则,可以用列表初始化来,示例:



[cpp]view plaincopy structFoo { intx; inty; //Foo(int,int,double){} protected: staticdoublez; }; int_tmain(intargc,_TCHAR*argv[]) { Foofoo{123,456};//如果写成Foo foo{123,456,789.0}会提示error C2078: 初始值设定项太多 cout<<foo.x<<""<<foo.y; return0; } 程序输出:123 456,因此可知静态数据成员的初始化是不能通过初始化列表来完成初始化的,它的初始化还是遵循以往的静态成员的初始化方式。


3、类含有基类或者虚函数

[cpp]view plaincopy structFoo { intx; inty; virtualvoidfunc(){}; }; int_tmain(intargc,_TCHAR*argv[]) { Foofoo{123,456}; cout<<foo.x<<""<<foo.y; return0; } 上例中类Foo中包含了一个虚函数,该程序也是非法的,编译不过的,错误信息和上述一样cannot convert from 'initializer-list' to 'Foo'。
[cpp]view plaincopy structbase{}; structFoo:base { intx; inty; }; int_tmain(intargc,_TCHAR*argv[]) { Foofoo{123,456}; cout<<foo.x<<""<<foo.y; return0; } 上例中则是有基类的情况,类Foo从base中继承,然后对Foo使用列表初始化,该程序也一样无法通过编译,错误信息仍然为cannot convert from 'initializer-list' to 'Foo',


4、类中不能有{}或者=直接初始化的费静态数据成员


[cpp]view plaincopy structFoo { intx; inty=5; }; int_tmain(intargc,_TCHAR*argv[]) { Foofoo{123,456}; cout<<foo.x<<""<<foo.y; return0; } 在结构体Foo中变量y直接用=进行初始化了,因此上述例子也不能使用列表初始化方法,需要注意的是在C++98/03中,类似于变量y这种直接用=进行初始化的方法是不允许的,但是在C++11中放宽了,是可以直接进行初始化的,对于一个类来说如果它的非静态数据成员使用了=或者{}在声明同时进行了初始化,那么它就不再是聚合类型了,不适合使用列表初始化方法了。


在上述4种不再适合使用列表初始化的例子中,需要注意的是一个类声明了自己的构造函数的情形,在这种情况下使用初始化列表是编译器是不会给你报错的,操作系统会给变量一个随机的值,这种问题在代码出BUG后是很难查找到的,因此这种情况不适合使用列表初始化需要特别注意,而其他不适合使用的情况编译器会直接报错,提醒你这些场景下使用列表初始化时不合法的。

那么是否有一种方法可以使得在类不是聚合类型的时候可以使用列表初始化方法呢?相信你肯定猜到了,作为一种很强大的语言不应该也不会存在使用上的限制。自定义构造函数+成员初始化列表的方式解决了上述类是非聚合类型使用列表初始化的限制。看下面的例子:


[cpp]view plaincopy structFoo { intx; inty=5; virtualvoidfunc(){} private: intz; public: Foo(inti,intj,intk):x(i),y(j),z(k){cout<<z<<endl;} }; int_tmain(intargc,_TCHAR*argv[]) { Foofoo{123,456,789}; cout<<foo.x<<""<<foo.y; return0; } 输出结果为 789 123 456 ,可见,尽管Foo中包含了私有的非静态数据以及虚函数,用户自定义构造函数,并且使用成员列表初始化方法可以使得非聚合类型的类也可以使用列表初始化方法,因此在这里给各位看官提个建议,在对类的数据成员进行初始化的时候尽量在类的构造函数中用成员初始化列表的方式来对数据成员进行初始化,这样可以防止一些意外的错误。
初始化列表

在上面的使得一个类成为非聚合类的例子2、3、4中,这些非法的用法编译器都报出的错误是cannot convert from 'initializer-list' to 'Foo',那么这个initializer-list是什么呢?为什么使用列表初始化方法是将initializer-list转换成对应的类类型呢?下面我们就来看看这个神秘的东西

1.任何长度的初始化列表

在C++11中,对于任意的STL容器都与未指定长度的数组有一样的初始化能力,如: [cpp]view plaincopy intarr[]={1,2,3,4,5}; std::map

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭