GEEK学习笔记— —C++11的新特性
2016-04-01 20:14
232 查看
long long 类型
长整型,最小是64位。列表初始化
花括号被用来进行变量的初始化,称之为列表初始化,在某些时候还可以为对象赋值。当对内置类型变量进行列表初始化且初始值存在丢失信息的可能时,编译器将报错。
long double ld = 3.1415926536; int a{ld}, b = {ld}; //错误:转换未执行,存在丢失信息可能 int c(ld), d = ld; //正确:转换执行,但丢失了部分值
也可以对容器进行初始化,如对vector初始化
vector<string> v1{"b","a","the"};
nullptr
以前我们习惯用NULL的预处理变量得到空指针,现在最好是用字面值nullptr来初始化空指针。constexpr
以前初始化const变量值的时候,我们是凭借经验用某个常量表达式去初始化,但是经常会出错。现在可以将变量声明为constexpr类型,让编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。一般来说,如果你认为变量是一个常量表达式,那就把它声明为constexpr类型。constexpr int mf = 20; //20是常量表达式 constexpr int mf = 20; //mf+1是常量表达式 constexpr int mf = 20; //只有当size是一个constexpr函数时才正确
constexpr函数是指能用于常量表达式的函数。定义constexpr函数的方法与其他函数类似,不过要遵循几项规定:函数的返回类型及所有形参的类型都是字面值类型,而且函数体中必须有且只有一条return语句:
constexpr int new_sz() {return 42;} constexpr int foo=new_sz(); //正确:foo是一个常量表达式
我们把new_sz定义成无参数的constexpr函数。因为编译器能在程序编译时验证new_sz函数的返回的是常量表达式,所有可以用new_sz函数初始化constexpr类型的变量foo。
执行该初始化任务时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr函数被隐式的定义为内联函数。
constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr函数中可以有空语句、类型别名以及using声明。
我们允许constexpr函数的返回值并非一个常量:
//如果cnt是常量表达式,则scale(arg)也是常量表达式 constexpr size_t scale(size_t cnt) {return new_sz()*cnt;}
当scale的实参是常量表达式时,它的返回值也是常量表达式:反之则不然:
int arr[scale(2)]; //正确:scale(2)是常量表达式 int i=2; //i不是常量表达式 int a2[scale(i)]; //错误:scale(i)不是常量表达式
如上例所示,当我们给scale函数传入一个形如字面值2的常量表达式时,它的返回类型也是常量表达式。此时,编译器用相应的结果值替换对scale函数的调用。
如果我们用一个非常量表达式调用scale函数,比如int类型的对象i,则返回值是一个非常量表达式。当把scale函数用在需要常量表达式的上下文中时,由编译器负责检查函数的结果是否符合要求。如果结果恰好不是常量表达式,编译器将发出错误信息。
using
以前是使用typedeftypedef double db;
现在可以使用关键字using进行别名声明,
using db = double;
auto类型
以前变量类型是特定的,如int,double。现在可以使用auto类型,auto能让编译器帮我们去分析表达式所属的类型,当然auto类型的变量必须初始化。auto i = val1 + val2; //假如val1和val2都是int类型,那么i也是int类型; //若val1是double类型,val1加上val2的值也是double类型,i就是double类型
要注意的一点是一条声明语句只能有一个基本数据类型
auto i = 0, *p = &i; //正确,i是整数、p是整型指针 auto sz = 0, pi = 3.14; //错误,sz和pi类型不同
当然简单的类型你还可以使用像int、double,像一些比较复杂的返回类型,建议使用auto,比如vector::const_iterator就可以直接用auto代替,即简单又安全。
又如
auto len = line.size(); //len的类型是string::size_type
decltype类型
auto类型通过表达式的值类型推断出变量类型并进行赋值,如果不需要赋值,只需要推断出变量类型,那么就可以使用decltype,它的作用就是选择并返回操作数的数据类型,只得到类型不计算表达式的值decltype(f()) sum = x; //sum的类型就是函数f的返回类型
编译器并不会调用函数f,而是使用当调用发生时f的返回类型作为sum的类型。
如果我们知道函数返回的指针将指向哪个数组,就可以使用decltype关键字声明返回类型。例如,下面的函数返回一个指针,该指针根据参数i的不同指向两个已知数组中的某一个:
int odd[]={1,3,5,7,9}; int even[]={0,2,4,6,8}; //返回一个指针,该指针指向含有5个整数的数组 decltype(odd) *arrPtr(int i) { return (i%2)?&odd:&even; //返回一个指向数组的指针 }
arrPtr使用关键字decltype表示它的返回类型是个指针,并且该指针所指的对象与odd的类型一致。因为odd是数组,所以arrPtr返回一个指向含有5个整数的数组的指针。有一个地方需要注意:decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想表示arrPtr返回指针还必须在函数声明时加一个*符号。
类内初始化
以前只有static const声明的整型成员能在类内部初始化,并且初始化值必须是常量表达式。现在都可以在类内部进行初始化操作。这里要注意只能使用花括号或等号,不能使用圆括号。范围for语句
以前遍历给定序列,一般是通过下标或者通过迭代器,设定起始位置、结束位置和步长。现在可以使用范围for语句,遍历给定序列的每个元素并对序列中的每个值执行操作,语法形式为:for (declaration : expression) statement
其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。
一个string对象表示一个字符的序列,可以作为expression部分。比如,把string对象中的字符每行一个输出:
string str("some string"); //每行输出str中的一个字符。 for (auto c : str) cout << c << endl;
又如进行操作:
string s("Hello World!!!"); //转换成大写形式 for (auto &c : s) c = toupper(c); cout << s << endl;
输出结果为 HELLO WORLD!!!
定义vector对象的vector
以前vector<vector<int> > //两个右尖括号之间有一个空格
现在
vector<vector<int>> //括号之间不需要括号了~
容器的cbegin和cend函数
如果对象只需读操作最好使用常量类型,为了方便得到const_iterator类型的返回值,新引入了两个函数,分别为cbegin和cend:vector<int> v(10); auto it = v.cbegin();
标准库函数begin和end
为了更安全更简单的使用指针,在标准库中加入了begin和end函数。与容器中的同名函数功能类似,这两个函数可以用在数组上,因为数组不是类类型,没有成员函数。使用形式是把数组当作它们的参数:int ia[] = {0,1,2,3,4,5,6,7,8,9}; int *beg = begin(ia); int *last = end(ia);
begin函数返回ia首元素指针,end函数返回ia尾元素下一位置的指针,它们定义在iterator头文件中。
除法舍入规则
作除法的时候,以前允许结果为负值的商向上或向下取整,现在规定一律向0取整(即直接切除小数部分)而取余运算,假如m%n不等于0,则它的符号和m相同,以前允许m%n的符号匹配n的符号,而且商向负无穷一侧取整。现在被禁止了,除了-m导致溢出的特殊情况,其他时候(-m)/n和m/(-n)都等于-(m/n),m%(-n)等于m%n,(-m)%n等于-(m%n)。具体示例如下:
21 % 6; //结果为3 21 / 6; //结果为3 21 % 7; //结果0 21 / 7; //结果3 -21 % -8; //结果-5 -21 / -8; //结果2 21 % -5; //结果1 21 / -5; //结果-4
initializer_list
如果函数的实参数量未知但是全部实参的类型都相同,我们可以使用initializer_list类型的形参,它定义在同名头文件中,和vector一样,initializer_list也是一种模板类型,提供如下操作initializer_list<T> lst; // 默认初始化,T类型元素的空列表 initializer_list<T> lst{a,b,c...}; // 初始化为初始值列表的副本,元素为const lst2(lst) // 拷贝或赋值不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素 lst2 = lst // 同上 lst.size() // 列表中的元素数量 lst.begin() // 返回指向lst中首元素的指针 lst.end() // 返回指向lst中尾元素下一位置的指针
下面给出一个例子,需要注意的是,含有initializer_list形参的函数也可以同时拥有其他形参。另外,如果想给initializer_list形参传递一个实参的序列,必须把序列放在一对花括号内:
string func(initializer_list<string> li) { string str(""); for(auto beg=li.begin(); beg!=li.end(); ++beg) str += *beg; return str; } int main() { cout << func({"This"," ","is"," ","C++"}) << endl; return 0; }
列表初始化返回值
函数可以返回花括号包围的值得列表。类似于其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。vector<string> process() { if (expected.empty()) return {}; else if (expected == actual) return {"func","Ok"}; else return {"func", expected, actual}; }
定义尾置返回类型
函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或者数组的引用。尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto://func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组 auto func(int i) ->int (*)[10];
因为我们把函数的返回类型放在了形参列表之后,所以可以清楚地看到func函数返回的是一个指针,并且该指针指向了含有10个整数的数组。
(未完待续~)
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性