C++11 新特性<一>

来源:互联网 发布:如何设置淘宝客优惠券 编辑:程序博客网 时间:2024/04/30 09:05

18.1.1 新类型

C++11 新增了类型 long long 和 unsigned long long 以支持 64位(或更宽)的整型;

新增了类型 char16_t 和 char32_t ,以支持 16位 和 32位的字符表示; 还增加了 “原始” 字符串。


18.1.2 统一的初始化

C++11 扩大了用大括号括起来的列表(初始化列表)的适用范围,使其可用于所有内置类型和用户定义的类型(即类对象)。

适用初始化列表时,可添加等号(=),也可不添加。

int x = {5};double y {3.14};short quar[5] {1, 2, 3, 4, 5};int * arr = new int[4] {2, 4, 6, 8};    //创建对象时class Stump{private:    int roots;    double weight;public:    Stump(int r, double w) : roots(r), weight(w) {}};    Stump s1(1, 11.1);  //old styleStump s2{2, 22.2};  //C++11Stump s3 = {3, 33.3};   //C++11


1.缩窄(防止缩窄)

初始化列表语法可防止缩窄,即禁止将数值赋给无法存储它的数值变量。

char c1 = 1.55e27;  // double-to-char, undefined behaviorchar c2 = 45968241; // int-to-char, undefined behavior    //禁止缩窄的类型转换char c3 {1.55e27};  //double-to-char, compile-time errorchar c4 = {45968241}; //int-to-char, out of range, compile-time error    //允许转换为更宽的类型char c5 {66};   //int-to-char, in range, alloweddouble c6 = {66}; //int-to-double, allowed

2.std::initializer_list

C++11 提供了模板类 std::initializer_list, 可将其用作构造函数的参数。

然而,如果类有将模板 std::initializer_list 作为参数的构造函数,则只有构造函数可以使用列表初始化形式。

列表中的元素必须是同一种类型或可以转换为同一种类型。

// STL 容器提供了将 std::initializer_list 作为参数的构造函数    vector<int> a1(10); // uninitialized vector with 10 elements    vector<int> a2{10}; // initializer-list, a2 has 1 element set to 10    vector<int> a3{1, 2, 3};    // 3 elements set to 1, 2, 3    /*    * 头文件 initializer_list 提供了对模板类 initializer_list 的支持。这个类包含成员函数 begin() 和 end(),    * 可用于获取列表的范围。除用于构造函数外,还可将 initializer_list 用作常规函数的参数:    */#include <initializer_list>    double sum(std::initializer_list<double> i1);    int main()    {        double total = sum( {2.5, 3.1, 4} );    // 4 converted to 4.0        ……    }        double sum(std::initializer_list<double> i1)    {        double tot = 0;        for (auto p = il.begin(); p != il.end() ; p++)            tot += p;        return tot;    }


18.1.3 声明

1> auto (自动类型推断)

auto maton = 112;// maton is type intauto pt = &maton;// pt is type int *double fm(double, int);auto pf = fm;// pf is type double (*) (double, int)// auto 简化模板声明for (std::initializer_list<double>::iterator p = il.begin(); p != il.end(); p++)//可替换为:for (auto p = il.begin(); p != il.end(); p++p)

2> decltype (将变量的类型声明为表达式指定的类型)

decltype(x) y;// 让 y 的类型与 x 相同, x 是一个表达式double x;int n;
decltype(x*n) q;// q same type as x*n, i.e., doubledecltype(&x) pd;// pd same as &x, i.e., double *//定义模板时特别有效,因为只有等到模板被实例化时才能确定类型template<typename T, typename U>void ef(T t, U u){decltype(T*U) tu;… …}// 其中 tu 将为表达式 T*U 的类型,这里假定定义了运算 T*U 。 例如,如果 T 为 char , U 为 short , 则 tu 将为 int 。// 指定的类型可以为 引用 和 constint j = 3;int &k = j;const int &n = j;decltype(n) i1;// i1 type const int &decltype(j) i2;// i2 type intdecltype((j)) i3;// i3 type int &decltype(k+1) i4;// i4 type int

3> 返回类型后置

C++11 新增了一种函数声明的语法:在函数名和参数列表后面(而不是前面)指定返回类型:

double f1(double, int);// traditional syntaxauto f2(double, int) -> double;// new syntax, return type is double//优点:可以使用 decltype 来指定模板函数的返回类型template<typename T, typename U>auto eff(T t, U u) -> decltype(T*U){. . .}// 这里解决的问题是:在编译器遇到 eff 的参数列表前, T 和 U 还不在作用于内,因此必须在参数列表后使用 decltype 

4> 模板别名:using=

typedef std::vector<std::string>::iterator itType;//C++11 提供了另一种创建别名的方法using itType = std::vector<std::string>::iterator;// typedef 与 using 区别: using 可用于模板部分具体化, 而 typedef 不能template<typename T>using arr12 = std::array<T, 12>;// template for multiple aliases// 上述语句 具体化 目标板 array<T, int>(将参数 int 设置为 12)。std::array<double, 12> a1;std::array<std::string, 12> a2;//可将它们替换成:arr12<double> a1;arr12<std::string> a2;

5> nullptr

简言之:使数据各司其职  之前 0 可以表示 int , 同时 0 还可以表示 空指针, nullptr 专一的表示空指针 , 为了兼容 代表空指针的用法不变 。

所以 表达式 nullptr == 0 为 true,      int 传参时可以体现出来, 传0没问题 ,但是 传 nullptr 就错了 ,这反映了 使用 nullptr 而不是 0 提供了更高的类型安全。


18.1.4 智能指针

C++11 摒弃了 auto_ptr , 并新增了三种智能指针:unique_ptr , shared_ptr 和 weak_ptr。


18.1.5 异常规范方面的修改

之前

void f501(int) throw(bad_dog); // can throw type bad_dog excetpion

void f733(long long) throw(); // doesn't throw an exception

与 auto_ptr 一样,C++ 编程社区的集体经验表明,异常规范的效果没有预期的好。因此,C++ 摒弃了异常规范。然而,标准委员会认为,

指出函数不会引发异常有一定的价值,他们为此添加了关键字 noexcept:

void f875(short, short) noexcept; //doesn't throw an exception


18.1.6 作用域内枚举

//由 class 或者 struct 定义。 解决了同一个作用域内 枚举成员同名的问题, 移植性问题 以及 类型检查低级问题enum Old1 {yes, no, maybe};// traditional formenum class New1 {never, sometimes, often, always};// new formenum struct New2 {never, lever, sever};// new form//新枚举要求进行显示限定,以免发生名称冲突。因此,引用特性枚举时,需要使用 New1::never 和 New2::never 等


18.1.7 对类的修改

1> 显示转换运算符

// C++ 引入了关键字 explicit , 以禁止单参构造函数导致的自动和转换class Plebe{Plebe(int);// automatic int-to-plebe conversionexplicit Plebe(double);//requires explicit use...};...Plebe a, b;a = 5;// implicit conversion, call Plebe(5)b = 0.5;// not allowedb = Plebe(0.5);// explicit conversion// C++11 拓展了 ecplicit 的这种用法,使得可对转换函数做类似的处理class Plebe{// conversion functionsoperator int() const;explicit operator double() const;
...};...Plebe a, b;int n = a;// int-to-plebe automatic conversiondouble x = b;// not allowedx = double(b);// explicit conversion, allowed

2>类内成员初始化

class Session{int mem1 = 10;double mem2 {1966.54};short mem3;public:Session(){}Session(short s) : mem3(s) {}Session(int n, double d, short s) : mem1(n), mem2(d), mem3(s) {}};// 注:可使用 等号 或者 大括号 版本的初始化,但不能使用 圆括号 版本的初始化。// 构造函数 初始化列表提供的值 会将默认值 覆盖 (即:覆盖类内成员初始化)



18.1.8 模板和 STL 方面的修改

1> 基于范围的 for 循环

double prices[5] = {4.1, 5.2, 6.3, 7.4, 8.5};for (double x : prices)     // 当然,这里的 double 可以用 auto 来替代    std::cout<<x<<std::endl;    std::vector<int> vi(6);for (auto &x : vi)    x = std::rand();    // 循环中修改元素内容 可使用引用类型

2> 新的 STL 容器

新增 forward_list(单项链表) , unordered_map , unordered_multimap , unordered_set , unordered_multiset

还增加了模板数组 array  std::array<int, 360> arr;   //arr of 360 ints


3> 新的 STL 方法

cbegin() 和 cend(); 简言之,元素具有 const 属性


4> valarray 升级

5> 摒弃 export

6> 尖括号

std::vector<std::list<int> > v1; //  避免 >> 与 > > 混淆

std::vector<std::list<int>> v1;  // ok in C++11


18.1.9 右值引用

C++11新增了右值引用,使用 && 表示。

右值引用可关联到右值,即可出现在赋值表达式的右边,但不能对其应用地址运算符的值。右值包括字面常量(C-风格字符串除外,他表示地址)、诸如 x+y 等表达式以及返回值的函数(条件是该函数返回的不是引用):

int x = 10;int y = 23;int && r1 = 13;int && r2 = x + y;double && r3 = std::sqrt(2.0);
注意:r2 关联到的是 x+y 得到的结果(即 33),即使 修改了 x 或者 y 的值,也不会影响到 r2。


有趣的是,将右值关联到右值引用导致该右值被存储到特定的位置,且可以获取该位置的地址。也就是说,虽然不能将运算符 & 用于 13, 但可将其用于 r1。 通过将数据与特定的地址关联,使得可以通过右值引用来访问该数据。参考如下代码:


double func(double tf) { return 5.0*(tf-32.0)/9.0; }

以下代码是在 XCode 下 init 函数 中写的代码  你完全可以 在 int main(){} 中写(如果 windows 下 支持的话)

double tc = 21.5;double && rd1 = 7.07;double && rd2 = 1.8 * tc + 32.0;double && rd3 = func(rd2);std::cout<<" tc value and address: "<<tc<<", "<<&tc<<std::endl;std::cout<<"rd1 value and address: "<<rd1<<", "<<&rd1<<std::endl;std::cout<<"rd2 value and address: "<<rd2<<", "<<&rd2<<std::endl;std::cout<<"rd3 value and address: "<<rd3<<", "<<&rd3<<std::endl;
运行结果:



0 0