0

C++11 新标准的内容

 2 years ago
source link: https://zasy.github.io/2018/10/29/c-11/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

c++11的新标准

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

    • char32_t and char16_t

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

    1. 扩大了大括号初始化的使用范围,可添加等号也可以不添加,
    2. 列表初始化的时候也可以用在new表达式中
    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 有用该类型修饰构造函数的参数的时候,初始化列表只能用该构造函数。并且这个修饰也可以修饰普通函数的参数,用于接受用大括号修饰的参数列表,并保持内容类型的一致性
  2. c++11 提供了多种简化声明的方式

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

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

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

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

    3. 模板别名:using =

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

      以前:typedef

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

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

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

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

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

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

  3. 自动的完成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不会报错
      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。 当不再内存分配内存时,不能使用智能指针
  4. c++11 抛弃了异常规范的指定,并且添加了关键字noexcept,指出不会引发异常的关键

  5. 作用域内的枚举

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

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

  6. 对类的修改

    1. 显示转换运算符

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

      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. 类内成员的初始化

      在类内定义中初始化成员

      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) {}
      }

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

  7. 模板和STL方面的修改

    1. 基于范围的for的循环

    2. 新的STL的容器

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

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

  8. 右值引用

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

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

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

  9. 移动语义和右值引用

    1. 为何需要移动语义?

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

      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中。
  10. 新的类功能

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

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

      • override

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

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

      • final

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

  11. Lambda函数

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

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

      1. bool f3(int x) {return x % 3 == 0;}
        int count3 = count_if(numbers.begin(), numbers.end(), f3); //指定函数对象为函数名
      2. 函数符是一个类对象,重载operator()()方法来实现一些功能。

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

        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中对于接受函数对象的函数,可以使用匿名函数定义其参数。

        int count3 = std::count_if(numbers.begin(), numbers.end(), [](int x){return x % 3 == 0;})
        • 使用[]替代了函数名;

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

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

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

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

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

      • function模板

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

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

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

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

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

    1. static_cast<>

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

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

      基本使用范围:

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

      example:

      #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:

      const int a= 0;

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

      const int *pi = &a;

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK