C++11 新标准的内容

c++11的新标准

  1. 新类型

    • 新增了long long 和 unsigned long long 两个类型(64位的整型)

    • char32_t and char16_t

    • 原始字符串
  2. 统一的初始化

    1. 扩大了大括号初始化的使用范围,可添加等号也可以不添加,
    2. 列表初始化的时候也可以用在new表达式中
    3. 大括号括起来来调用类的构造函
    1
    2
    3
    Stump s1(3, 15.6); //old style
    Stump s2{5, 43,4}; // C++11
    Stump s3 = {4, 32.1}; //C++11

    好处:

    • 缩窄:防止将数值赋给无法存储它的数值变量,但允许准换为更宽的类型
    • Std::initializer_list 有用该类型修饰构造函数的参数的时候,初始化列表只能用该构造函数。并且这个修饰也可以修饰普通函数的参数,用于接受用大括号修饰的参数列表,并保持内容类型的一致性
  3. 声明

    c++11 提供了多种简化声明的方式

    1. auto

    2. decltype 将变量的类型声明为表达式的类型

      1
      2
      3
      4
      // 构造一个比较的匿名函数
      auto cmp = [](int left, int right) { return (left ^ 1) < (right ^ 1);};
      // 声明 优先级队列的时候, 简化具体化模版类的类型的输入
      std::priority_queue<int, std::vector<int>, decltype(cmp)> q3(cmp);
    3. 返回类型后置

      1
      2
      double f1(double, int); // traditional syntax
      auto f2(double, int) -> double; // new syntax, return type is double

      帮助模板函数指定返回类型,帮助编译器在遇到参数列表后,再去指定根据参数类型指定返回类型。

    4. 模板别名:using =

      对于冗长的标识符,方便的创造别名。

      以前:typedef

      1
      typedef std::vector<std::string>::iterator itType;

      C++11提供的方式:using =

      1
      using itType = std::vector<std::string>::iterator;

      差别在于,新语法可以用于模板的具体化

      1
      2
      template<typename T>
      using arr12 = std::array<T, 12>;
    5. nullptr

      c++11 新增了关键字nullptr,用于表示空指针,这样可以避免以前0即表示空指针,又表示整数常量的问题。

  4. 智能指针

    自动的完成delete的工作。首先引入了auto_ptr,以帮助自动化的完成这些工作,发展过程中,需要一些更加精致的机制,新增了三个智能指针 unique_ptrshared_ptrweak_ptr。当指针过期的时候,期西沟函数将调用delete函数的运行来释放内存,防止内存泄漏。

    1. 使用智能指针

      • 导入memory都文件 #include <memory>
      • 使用智能指针的模板具体化实例指向新生成的对象 unique_ptr<string> ps = new string("test");
      • 不需要delete去删除生成的内存
      • 对于非堆内存是禁止使用智能指针去删除对应的内存的string vacation("what a happy day!!!")
    2. 为什么有三种智能指针

      当相同类型的智能指针相互赋值的时候,一块内存就会被删除多次,所以这样有很多种策略

      • 执行深复制;
      • 建立所有权(ownership)的概念,只有一个指针可以拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。然赋值操作去转让所有权。转让所有权之后该智能指针就不指向该内存了,就为空了,之后访问就会出现问题,auto_ptr就没有unique_ptr那么的严格;
      • 跟踪和引用特定对象的智能指针数,这里是引用计数,当最后一个指针过期时,才调用delete。
    3. 为什么unique_ptr要优于auto_ptr?

      • 当不合格使用unique_ptr的赋值的时候,会报错,而auto_ptr不会,运行时会发生错误
      • 作为临时右值赋给函数返回的临时变量时,unique_ptr不会报错
      1
      2
      3
      4
      5
      6
      using namespace std;
      unique_ptr<string> pu1(new string " Hi bo!");
      unique_ptr< string> pu2;
      pu2 = pu1; // not allowed;
      unique_ptr<string> pu3;
      pu3 = unique_ptr<string>(new string "Yo!"); // allowed
      • unique_ptr支持使用std::move(),安全的执行智能指针的赋值操作
    4. 如何使用智能指针?

      • 对个指向同一对象的指针 使用shared_ptr
      • 不需要指向同一个对象的指针 使用unique_ptr
      • new分配内存时,才能使用 auto_ptr 和 shared_ptr。 new [] 和 new 分配内存的时候都可以使用 unique_ptr。 当不再内存分配内存时,不能使用智能指针
  5. c++11 抛弃了异常规范的指定,并且添加了关键字noexcept,指出不会引发异常的关键

  6. 作用域内的枚举

    • 不同的实现采用不同的底层类型,之前的底层类型都是整形类型。

    • 枚举名的作用域为枚举定义所属的作用域,所以相同的作用域内的两个枚举,枚举变量名不能是相同的枚举名。新的枚举使用class或struct定义

  7. 对类的修改

    1. 显示转换运算符

      原因:自动类型转换可能引起各种问题,c++引入老人关键词explicit,以静止单参数构造函数导致的自动类型转换

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      class Plebe{
      Plebe(int); //automatic int-to-plebe conversion
      explicit Plebe(double); // required explicit use
      ...
      };

      Plebe a, b;
      a = 5; //implicit conversion, call Plebe(5)
      b = 0.5; // not allowed
      b = Plebe(0.5); // allow explicit conversion

      c++11拓展了explicit的用法,使得可对转换函数做类似的处理

    2. 类内成员的初始化

      在类内定义中初始化成员

      形式:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      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) {}
      }

      好处:使用该初始化方式可以有效的减少代码量

  8. 模板和STL方面的修改

    1. 基于范围的for的循环

    2. 新的STL的容器

      • forward_list 单向链表
      • unordered_map,unordered_set 以及对应的multimap 或者multiset
      • 新增的模板array
    3. valarray升级

    4. 尖括号

      需要和运算符>>区分开,所以声明嵌套的模板时,需要使用空格将尖括号分开

  9. 右值引用

    • 传统的c++引用,现在被称为左值引用,使得标识符关联到左值。(表示数据的表达式,变量名 或解除引用的指针),程序可获取其地址。

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

      1
      2
      3
      4
      5
      int x = 10;
      int y = 23;
      int && r1 = 13;
      int && r2 = x + y;
      double && r3 = std::sqrt(2.0);
    • 引用右值引用的主要目的就是用于移动语义

  10. 移动语义和右值引用

    1. 为何需要移动语义?

      移动语义避免了移动原始数据,而只是修改了记录,由于右值引用是临时变量,右值引用才可以引用该临时能量。该临时能量的值可以直接被窃取而返回,所以减少移动原始数据。

      1
      2
      3
      Useless two = one; //calls copy constructor

      Useless four(one + three); //临时变量作为输入,由于声明了移动构造函数,所以可以直接调用移动语义 calls move constructor
    2. 在之前没有改移动语义的出现,是编译器的智能编译实现的优化

    3. static_cast<>可将对象的类型强制转换为右值,转换为右值后可以使用移动构造函数和移动赋值运算符
    4. 使用移动构造函数和普通的复制构造函数的主要区别是,输入变量是左值还是右值,左值还可以被调用,所以还有用处,而右值一般是临时的,不在被调用的,存储在一个临时的特殊位置,所以一般不会被再次调用,所以可以将其直接转移,减少复制。而static_cast<>可以将左值转换为右值。
    5. C++11 提供了更简单的std::move()的方式,在头文件utility中。
  11. 新的类功能

    1. C++11新增了移动构造函数和移动赋值运算符
    2. 可以使用默认的成员函数: default 或者禁用默认的成员方法: delete

    3. 管理虚方法的标识符: override 和 final

      • override

        1
        2
        3
        virtual void f(char* ch) const override { 
        std::cout << val() << ch << "!\n";
        }

        使用虚说明符override指出您要覆盖一个虚函数:如果声明出现问题,不能覆盖基类的虚函数的话就会产生报错

      • final

        禁止派生类覆盖特定的虚方法,就会使用final虚函数说明符,放在虚函数的末尾

  12. Lambda函数

    1. 比较函数指针、函数符和Lambda函数

      首先将这三者都可以统称为函数对象。举例说明,完成一个函数对象,该函数对像需要返回该整数是否被可以被指定的整数整除:

      1. 函数名:

        1
        2
        bool f3(int x) {return x % 3 == 0;}
        int count3 = count_if(numbers.begin(), numbers.end(), f3); //指定函数对象为函数名
      2. 函数符:

        函数符是一个类对象,重载operator()()方法来实现一些功能。

        好处:可以使用同一个函数符来完成两项的计数的任务

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        class f_mod {
        private:
        int dv;
        public:
        f_mod(int d = 1): dv(d) {}
        bool operator()(int x) {return x % dv == 0};
        }

        f_mod obj(3);

        bool is_div_by_3 = obj(7); // 运行重载之后的operator() () 函数

        int count3 = std::count_if(number.begin(), number.end(), f_mod(3));
      3. Lambda函数

        c++11中对于接受函数对象的函数,可以使用匿名函数定义其参数。

        1
        int count3 = std::count_if(numbers.begin(), numbers.end(), [](int x){return x % 3 == 0;})

        区别:

        • 使用[]替代了函数名;

        • 没有声明返回的类型, (相当于隐性的使用了decltyp得到的类型)

        • 仅当lambda表达式完全又一条语句构成的时候,才可以不指定返回类型,比如上面的情况;否则需要使用新增的返回类型后置的语法

          1
          [](double x)->double{int y = x; return y - x;} // 返回类型后置
        1. 为什么要使用Lambda
          • 距离: 让定义位于使用的地方
          • 简洁: 函数符代码比函数名和lambda代码更加繁琐
          • 效率: 函数地址的方法以为着是非内联的函数,而函数类型和lambda函数不会阻止内联
          • 功能:lambda函数可以访问作用域内的任何动态变量,要捕获要使用的变量,将其名称放入中括号内。
            • 如果只指定了函数名, 如[z],将按值访问变量;
            • 名称前加上&,如[&count], 将按引用的访问count;
            • [&],按引用访问所有的变量
            • [=], 按值访问所有的动态变量
            • 也支持混合使用
  13. 包装器 — 引申出适配器模式

    c++提供对个包装器。这些对象用于给其他编程接口提供更一致或更合适的接口。C++提供不同的包装器模板,bind、mem_fn和reference_wrap以及包装器function。

    1. 包装器functiong和模板的低效性

      • function模板

        1
        std::function<double(char, int)> fdci;

        从调用特征表的角度定义了一个对象,可用于包装调用特征标相同的函数指针、函数对象和lambda表达式。

        1
        2
        function<double(double)> ef1 = dub;
        function<double(double)> ef2 = square;

        可以将function<double(double)>作为函数的形参,让形参f的类型与原始实参相匹配

  14. c++11中的四种类型转换

    1. static_cast<>

      定义: Converts between types using a combination of implicit and user-defined conversions.

      完成编译器允许的隐式类型转换

      基本使用范围:

      1. 基本数据类型之间的转换
      2. 派生类体系的向上转型

      example:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      #include <vector>
      #include <iostream>

      struct B {
      int m = 0;
      void hello() const {
      std::cout << "Hello world, this is B!\n";
      }
      };
      struct D : B {
      void hello() const {
      std::cout << "Hello world, this is D!\n";
      }
      };

      enum class E { ONE = 1, TWO, THREE };
      enum EU { ONE = 1, TWO, THREE };

      int main()
      {
      // 1: initializing conversion
      int n = static_cast<int>(3.14);
      std::cout << "n = " << n << '\n';
      std::vector<int> v = static_cast<std::vector<int>>(10);
      std::cout << "v.size() = " << v.size() << '\n';

      // 2: static downcast
      D d;
      B& br = d; // upcast via implicit conversion
      br.hello();
      D& another_d = static_cast<D&>(br); // downcast
      another_d.hello();

      // 3: lvalue to xvalue
      std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
      std::cout << "after move, v.size() = " << v.size() << '\n';

      // 4: discarded-value expression
      static_cast<void>(v2.size());

      // 5. inverse of implicit conversion
      void* nv = &n;
      int* ni = static_cast<int*>(nv);
      std::cout << "*ni = " << *ni << '\n';

      // 6. array-to-pointer followed by upcast
      D a[10];
      B* dp = static_cast<B*>(a);

      // 7. scoped enum to int or float
      E e = E::ONE;
      int one = static_cast<int>(e);
      std::cout << one << '\n';

      // 8. int to enum, enum to another enum
      E e2 = static_cast<E>(one);
      EU eu = static_cast<EU>(e2);

      // 9. pointer to member upcast
      int D::*pm = &D::m;
      std::cout << br.*static_cast<int B::*>(pm) << '\n';

      // 10. void* to any type
      void* voidp = &e;
      std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
      }
    2. dynamic_cast

      定义:Safely converts pointers and references to classes up, down, and sideways along the inheritance hierarchy.

      用法:dynamic_cast <new_type> (expression)

      If the cast is successful, dynamic_cast returns a value of type new_type.

      If the cast fails and new_type is a reference type,it throws an exception that matches a handler of type std::bad_cast.

    3. const_cast

      定义:对指针或引用去除或者添加const属性

      example:

      1
      2
      3
      4
      5
      6
      7
      const int a= 0;

      int b = const_cast<int>(a);//不对的

      const int *pi = &a;

      int * pii = const_cast<int *>pi;//去除指针中的常量性,也可以添加指针的常量性;
    4. reinterpret_cast

      定义: 无视类型的引用之间的转换