十一、vector对象的列表初始化
c++11新标准提供了另外一种为vector对象的元素赋初值的方法,即列表初始化。此时,用花括号括起来的0个或多个元素值被赋给vector对象:
vector<string> v1 = {"a", "an", "the"}; //列表初始化
vector<string> v2 = ("a", "an", "the"); //错误
十二、容器的cbegin和cend函数
begin和end返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator,如果对象不是常量,返回iterator:
vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); //it1的类型是vector<int>::iterator
auto it2 = cv.begin(); //it2的类型是vector<int>::const_iterator
有时候这种默认的行为并非我们所要,如果对象只需读操作而无需写操作的话最好使用常量类型(比如const_iterator)。为了便于专门得到
const_iterator类型的返回值,C++11新标准引入了两个新函数,分别是cbegin和cend:
auto it3 = v.cbegin(); //it3的类型是vector<int>::const_iterator
类似于begin和end,上述两个新函数也分别返回指数容器第一个元素或者最后元素下一位置的迭代器。有所不同的是,不论vector对象(或string对象)本身是否是常量,返回值都是const_iterator。
十三、标准库begin和end函数
尽管能计算得到尾后指针,但这种用法极易出错。为了让指针的使用更简单、更安全,c++11新标准引入了两个名为begin和end的函数。这两个函数与容器中的两个同名成员功能类似,不过数组毕竟不是类类型,因此这两个函数不是成员函数,正确的使用形式是将数组作为它们的参数:
int ia[] = {0,1,2,3,4,5,6,7,8,9}; //ia是一个含有十个整数的数组
十四、除法的舍入规则
在除法运算中,如果两个运算对象的符号相同则为正(如果不为0的话),否则商为负。C++早期版本允许结果为负值的商向上或向下取整,C++11新标准规定商一律向0取整(即一律切除小数部分)
十五、用大括号包围的值列表赋值
C++11新标准允许使用花括号括起来的初始值列表作为赋值语句的右侧运算对象;
k = {3.14}; //错误:窄化转换
vector<int> vi; //初始为空
vi = {0,1,2,3,4,5,6,7,8,9}; //vi现在含义10个元素,值从0到9
如果左侧对象是内置类型,那么初始值列表最多只能包含一个值,而且该值即使转换的话其所占空间也不应该大于目标类型的空间。
对于类类型来说,赋值运算的细节由类本身决定,对于vector来说,vector模板重载了赋值运算符并且可以接收初始值列表,当赋值发生时用右侧对象的元素替换左侧对象的元素。
无论左侧对象的类型是什么,初始值列表都可以为空。此时,编译器创建一个值初始化的临时量并将其赋给左侧运算对象。
十六、将sizeof用于类成员
C++11新标准允许我们使用作用域运算符来获取类成员的大小。通常情况下只有通过类的对象才能访问到类的成员,但是sizeof运算符无需我们提供一个具体的对象,因为想知道类成员的大小无需真的获取该成员。
sizeof运算符的结果部分第依赖于其作用的类型:
·对char或类型为char的表达式执行sizeof运算,结果得1.
·对引用类型执行sizeof运算得到被引用对象所占空间的大小。
·对指针执行sizeof运算得到指针本身所占空间的大小。
·对解引用执行sizeof运算得到指针指向的对象所占空间的大小,指针不需有效。
·对数组执行sizeof运算得到整个数组所占空间的大小,等价于对数组中的所有元素各执行一次sizeof运算并将所得结果求和。注意,sizeof运算不会把数组转换成指针来处理
·对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小因为执行sizeof运算能得到整个数组的大小,所以可以用数组的大小除以单个元素的大小得到数组中元素的个数:
//sizeof(ia)/sizeof(*ia) 返回ia的元素数量
constexpr size_t sz = sizeof(ia)/sizeof(*ia);
int arr2[sz]; //正确:sizeof返回一个常量表达式
十七、范围for语句
C++11新标准引入了一种更简单的for语句,这种语句可以遍历容器或其他序列的所有元素。范围for语句的语法形式是:
for(declaration : expression)
statement
expression表示的必须是一个序列,比如用花括号括起来的初始值列表、数组、或者vector或者string等类型的对象,这些类型的共同点是拥有能返回迭代器的begin和end成员
declaration定义一个变量,序列中的每个元素都能转换成该变量的类型。确保类型相容最简单的方法是使用auto类型说明符,这个关键字可以令编译器帮助我们指定合适的类型。
如果要对序列中的元素执行写操作,循环变量必须声明成引用类型。
vector<int> v = {0,1,2,3,4,5,6,7,8,9};
//范围变量必须是引用类型,这样才能对元素执行写操作
for(auto &r : v) //对v中的每一个元素
r *= 2;
与之等价的传统for语句:
for(auto begin = v.begin(), end = v.end(); begin != end; begin++ )
auto &r = *begin;
r *= 2;
十八、标准库initializer_list类
如果函数实参数量未知,但是全部实参的类型相同,我们可以使用initializer_list类型的形参。initializer_list是一种标准库类型,用于表示某种特定类型的数组。
initializer_list类型定义在同名的头文件中,和vector一样,initializer_list也是一种模板类型,定义initializer_list对象时,必须说明列表中所含元素的类型:
initializer_list<string> ls; //initializer_list的元素类型是string
initializer_list<int> li; //initializer_list元素类型是int
和vector不一样的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值
void error_msg(initializer_list<string> il){
for(auto beg = il.begin(); beg != il.end(); ++beg){
cout<<*beg <<endl;
}
}
如果想向initializer_list形参中传递一个值的序列,必须把序列放在一个大括号内
//expected和actual是string对象
if(expected != actual){
error_msg({"functionX", expected, actual});
}else{
error_msg({"functionX", "okay"});
}
十九、列表初始化返回值
c++11新标准规定,函数可以返回括号包围的值的列表。类型于其他返回结果,此处的列表也用来对表示函数返回值的临时量进行初始化。如果列表为空,临时量执行值初始化。否则,返回值由函数的返回类型决定。
vector<string> process(){
//...
//expect 和 actual是string对象
if(expect.empty())
return {}; //返回一个空vector对象
else if (expect == actual)
return {"functionX", "okay"}; //返回列表初始化的vector对象
else
return {"functionX", expect, actual};
}
第一条return语句返回一个空列表,此时,process函数返回的vector对象是空的。如果expect不为空,返回的vector对象分别用两个或三个元素初始化
二十、定义尾置返回类型
C++11中定义了尾置返回类型(trailing return type),任何函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或者数组的引用。
尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放一个auto:
//func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组
auto func(int i ) ->int(*)[10];
因为我们把函数的返回类型放在了形参列表之后,所以可以清楚地看到func函数返回的是一个指针,并且该指针指向了含有10个整数的数组。