GEEK学习笔记— —C++11的新特性

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

以前是使用typedef

typedef 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个整数的数组。

(未完待续~)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值