C++11之initialization_list
来源:互联网 发布:主角创造人工智能小说 编辑:程序博客网 时间:2024/05/22 13:27
转自http://blog.csdn.net/hailong0715/article/details/54018002
在我们实际编程中,我们经常会碰到变量初始化的问题,对于不同的变量初始化的手段多种多样,比如说对于一个数组我们可以使用 int arr[] = {1,2,3}的方式初始化,又比如对于一个简单的结构体:
- struct A
- {
- int x;
- int y;
- }a={1,2};
统一的初始化方法
在C++98/03中我们只能对普通数组和POD(plain old data,简单来说就是可以用memcpy复制的对象)类型可以使用列表初始化,如下:数组的初始化列表: int arr[3] = {1,2,3}
POD类型的初始化列表:
- struct A
- {
- int x;
- int y;
- }a = {1,2};
- class Foo
- {
- public:
- Foo(int) {}
- private:
- Foo(const Foo &);
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo a1(123); //调用Foo(int)构造函数初始化
- Foo a2 = 123; //error Foo的拷贝构造函数声明为私有的,该处的初始化方式是隐式调用Foo(int)构造函数生成一个临时的匿名对象,再调用拷贝构造函数完成初始化
- Foo a3 = { 123 }; //列表初始化
- Foo a4 { 123 }; //列表初始化
- int a5 = { 3 };
- int a6 { 3 };
- return 0;
- }
同时列表初始化方法也适用于用new操作等圆括号进行初始化的地方,如下:
- int* a = new int { 3 };
- double b = double{ 12.12 };
- int * arr = new int[] {1, 2, 3};
列表初始化的一些使用细节
虽然列表初始化提供了统一的初始化方法,但是同时也会带来一些使用上的疑惑需要各位苦逼码农需要注意,比如对下面的自定义类型的例子:- struct A
- {
- int x;
- int y;
- }a = {123, 321};
- //a.x = 123 a.y = 321
- struct B
- {
- int x;
- int y;
- B(int, int) :x(0), y(0){}
- }b = {123,321};
- //b.x = 0 b.y = 0
那么如何区分一个类(class struct union)是否可以使用列表初始化来完成初始化工作呢?关键问题看这个类是否是一个聚合体(aggregate),首先看下C++中关于类是否是一个聚合体的定义:
(1)无用户自定义构造函数。
(2)无私有或者受保护的非静态数据成员
(3)无基类
(4)无虚函数
(5)无{}和=直接初始化的非静态数据成员。下面我们逐个对上述进行分析。
1、首先存在用户自定义的构造函数的情况,示例如下:
- struct Foo
- {
- int x;
- int y;
- Foo(int, int){ cout << "Foo construction"; }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo foo{ 123, 321 };
- cout << foo.x << " " << foo.y;
- return 0;
- }
可以看出对于有用户自定义构造函数的类使用初始化列表其成员初始化后变量值是一个随机值,因此用户必须以用户自定义构造函数来构造对象。
2、类包含有私有的或者受保护的非静态数据成员的情况
- struct Foo
- {
- int x;
- int y;
- //Foo(int, int, double){}
- protected:
- double z;
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo foo{ 123,456,789.0 };
- cout << foo.x << " " << foo.y;
- return 0;
- }
- struct Foo
- {
- int x;
- int y;
- //Foo(int, int, double){}
- protected:
- static double z;
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo foo{ 123,456};
- cout << foo.x << " " << foo.y;
- return 0;
- }
3、类含有基类或者虚函数
- struct Foo
- {
- int x;
- int y;
- virtual void func(){};
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo foo {123,456};
- cout << foo.x << " " << foo.y;
- return 0;
- }
- struct base{};
- struct Foo:base
- {
- int x;
- int y;
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo foo {123,456};
- cout << foo.x << " " << foo.y;
- return 0;
- }
4、类中不能有{}或者=直接初始化的费静态数据成员
- struct Foo
- {
- int x;
- int y= 5;
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo foo {123,456};
- cout << foo.x << " " << foo.y;
- return 0;
- }
在上述4种不再适合使用列表初始化的例子中,需要注意的是一个类声明了自己的构造函数的情形,在这种情况下使用初始化列表是编译器是不会给你报错的,操作系统会给变量一个随机的值,这种问题在代码出BUG后是很难查找到的,因此这种情况不适合使用列表初始化需要特别注意,而其他不适合使用的情况编译器会直接报错,提醒你这些场景下使用列表初始化时不合法的。
那么是否有一种方法可以使得在类不是聚合类型的时候可以使用列表初始化方法呢?相信你肯定猜到了,作为一种很强大的语言不应该也不会存在使用上的限制。自定义构造函数+成员初始化列表的方式解决了上述类是非聚合类型使用列表初始化的限制。看下面的例子:
- struct Foo
- {
- int x;
- int y= 5;
- virtual void func(){}
- private:
- int z;
- public:
- Foo(int i, int j, int k) :x(i), y(j), z(k){ cout << z << endl; }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo foo {123,456,789};
- cout << foo.x << " " << foo.y;
- return 0;
- }
初始化列表
在上面的使得一个类成为非聚合类的例子2、3、4中,这些非法的用法编译器都报出的错误是cannot convert from 'initializer-list' to 'Foo',那么这个initializer-list是什么呢?为什么使用列表初始化方法是将initializer-list转换成对应的类类型呢?下面我们就来看看这个神秘的东西
1、任何长度的初始化列表
- int arr[] = { 1, 2, 3, 4, 5 };
- std::map < int, int > map_t { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
- std::list<std::string> list_str{ "hello", "world", "china" };
- std::vector<double> vec_d { 0.0,0.1,0.2,0.3,0.4,0.5};
- struct Foo
- {
- int x;
- int y;
- int z;
- Foo(std::initializer_list<int> list)
- {
- auto it= list.begin();
- x = *it++;
- y = *it++;
- z = *it++;
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Foo foo1 {123,456,789};
- Foo foo2 { 123, 456};
- Foo foo3{ 123};
- Foo foo4{ 123, 456, 789,258 };
- cout << foo1.x << " " << foo1.y << " " << foo1.z<<endl;
- cout << foo2.x << " " << foo2.y << " " << foo2.z << endl;
- cout << foo3.x << " " << foo3.y << " " << foo3.z << endl;
- cout << foo4.x << " " << foo4.y << " " << foo4.z << endl;
- return 0;
- }
- 程序的输出结果为:
- 123 456 789
- 123 456 -858993460
- 123 -858993460 -858993460
- 123 456 789
- class FooVec
- {
- public:
- std::vector<int> m_vec;
- FooVec(std::initializer_list<int> list)
- {
- for (auto it = list.begin(); it != list.end(); it++)
- m_vec.push_back(*it);
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- FooVec foo1 { 1, 2, 3, 4, 5, 6 };
- FooVec foo2 { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
- return 0;
- }
std::initialzer-list不仅可以用于自定义类型的列表初始化方法,也可以用于传递相同类型数据的集合:
- void func(std::initializer_list<int> list)
- {
- for (auto it = list.begin(); it != list.end(); it++)
- {
- cout << *it << endl;
- }
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- func({});//传递一个空集
- func({ 1, 2, 3 });//传递int类型的数据集
- return 0;
- }
2、std::initialzer-list的使用细节
- std::initializer_list<int> list_t ={ 1, 2, 3, 4 };
- int _tmain(int argc, _TCHAR* argv[])
- {
- for (auto it = list_t.begin(); it != list_t.end; it++)
- (*it) = 1;
- return 0;
- }
此外initialzer-list<T>保存的是T类型的引用,并不对T类型的数据进行拷贝,因此需要注意变量的生存期。比如我们不能这样使用:
- std::initializer_list<int> func(void)
- {
- auto a = 2, b = 3;
- return{ a, b };
- }
列表初始化防止类型收窄
C++11的列表初始化还有一个额外的功能就是可以防止类型收窄,也就是C++98/03中的隐式类型转换,将范围大的转换为范围小的表示,在C++98/03中类型收窄并不会编译出错,而在C++11中,使用列表初始化的类型收窄编译将会报错:- int a = 1.1; //OK
- int b{ 1.1 }; //error
- float f1 = 1e40; //OK
- float f2{ 1e40 }; //error
- const int x = 1024, y = 1;
- char c = x; //OK
- char d{ x };//error
- char e = y;//error
- char f{ y };//error
上面例子看出,用C++98/03的方式类型收窄并不会编译报错,但是将会导致一些隐藏的错误,导致出错的时候很难定位,而利用C++11的列表初始化方法定义变量从源头了遏制了类型收窄,使得不恰当的用法就不会用在程序中,避免了某些位置类型的错误,因此建议以后再实际编程中尽可能的使用列表初始化方法定义变量。
- C++11之initialization_list
- usb-skeleton.c 之 11---skel_read
- 读书笔记之c和指针(11)
- C/C++之习题11-15
- C语言之旅(11)链表
- C语言之认识C
- C语言之四书五经
- C语言之四书五经
- C语言之四书五经
- C语言之数据类型
- C语言之指针
- 正则表达式之C#
- C语言之解析
- .net学习之c#
- c#之路
- C语言之诡异
- C#、Java之比较
- 学习笔记之c
- Java反射机制操作对象赋值
- 职业规划---菜鸟级
- FL Studio12汉化版教程之如何将音轨转录为波形文件
- 动画大全:nineoldandroids
- 定时任务命令crontab
- C++11之initialization_list
- 两边横线,中间标题
- python 多个字符串合并操作"a" 'b' "c"="a"+'b'+"c"=abc
- Liunx中分区过程
- 想使用Docker容器?先看看这些注意事项
- .net core2.0下使用Identity改用dapper存储数据
- ASP.NET Core 认证与授权[6]:授权策略是怎么执行的?
- springboot中使用Mybatis注解配置详解
- Qt之自定义控件阴影