• 5. C++11


    C++2.0新特性

    第一讲 语言

    1. C++标准的演化

      • C++98(1.0)
      • C++03(TR1,Technical Report1)
      • C++1(2.0)
      • C++14
    2. 标准库新特性

      • 新的标准库在#include时可以不带.h
      • 旧式的C头文件名仍然可以使用.h
    3. 标准库版本检测#define __cplusplus 201103L,编译器通常需要打开ISO11使用

    4. Variadic Templates是通过模板参数特征进行递归分化调用处理

      void print(){}// 递归终点函数,必须实现
      // 参数的数量和类型不确定
      template   
      void print(const T& firstArg, const Type&...args){
      	cout<
      void print(const Type&...args){
          
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    5. 类参数的递归继承tuple

      template class tuple;
      template<>class tuple<>{};
      // 递归到终点执行上面的
      template
      class tuple
          :private tuple
      {
      	typedef tupleinherited;
       public:
          tuple(){}
          tuple(Head v, Tail... vtail)
              :m_head(v), inherited(vtail...){}
          typename Head::type head(){return m_head;}
          inheried& tail(){return *this;}
       protected:
          Head m_head;
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    6. 模板空格

      vector >; // 传统C++需要空格
      vector>;  // C++11后不需要空格
      
      • 1
      • 2
    7. C++11可以使用nullptr代替0或者NULL,是一种指针类型

    8. 变量类型自动推导关键词auto

      // 不使用auto
      list c;
      list::iterator ite;
      ite = find(c.begin(), c.end(), target);
      // 使用auto
      list c;
      auto ite = find(c.begin(), c.end(), target);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    9. C++11支持一致性初始化,在变量后使用大括号进行赋值

      int value[]{1, 2, 3};
      vector v{2, 3, 5, 6};
      vector cities{"Berlin", "New York", "London"};
      complexc{4.0, 3.0};
      
      • 1
      • 2
      • 3
      • 4

      编译器看到{ }便会做到一个initializer_list,它关联到一个array。调用函数时,该array中的元素会被编译器逐一分解传给函数。但如果调用的函数是initializer_list类型,调用者不能给与数个参数,然后以为它们会自动转为一个initializer_list传入

      • 传参对象接受initializer_list,则可以直接将参数整体传入
      • 传参对象不接受initializer_list,则需要将参数分解传入
    10. initializer_list的示例

      #include 
      int i;	// 变量声明
      int j{};// 变量声明并初始化为0
      int *q{};// 变量声明并初始化为nullptr
      int x1(5.3);
      int x2 = 5.3;
      int x3{5.3}; // error, 不可以进行低类型的自动转换
      
      // 参数个数不定的函数调用
      void print(std::initializer_list vals){
          for(auto p = vals.begin(); p != vals.end(); ++p){
              std::cout << *p <<  "\n";
          }
      }
      
      print({1,2,2,3}); // 参数数量不确定但是类型相同
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    11. initializer_list本质是一个存储参数列表得到array容器,只存储array的头部指针和大小,只能进行浅拷贝

    12. explicit通常只修饰构造函数,表示对象的调用不能进行隐式转换

    13. non-explicit one argument construction才能做隐式转换

      struct Comoplex{
          int real, imag;
          Complex(int re, int im = 0):real(re), imag(im)
          {}
          Complex operator+(const Complex& x){
              return Complex((real + x.real), (imag + x.imag));
          }
      };
      Complex c1(12.5);
      Complex c2 = c1 + 5;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    14. range-based for statement

      // 语法
      for(decl : coll){// (迭代变量:容器),将容器内的变量顺序给迭代变量赋值
      	statement;
      }
      // 本质
      for(auto _pos = coll.begin(), end = coll.end(); _pos != _end; ++_pos){
          decl = *_pos;
          statement;
      }
      // 示例 
      for(int i: {2,3,4,56,5}){
          cout << i << endl;
      }
      // 改变元素内容并且快速,尽量使用reference
      vector vec;
      for(auto& elem : vec){
          elem *= 3;
      }
      // 关联式容器不可使用迭代器直接改变元素内容,所以需要使用
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
    15. 如果自行定义了一个ctor(构造函数),那么编译器就不会再给你一个默认的构造函数(不需要实参即可进行调用),在构造函数后写一个=default就可以重新获得并使用default ctor

    16. =default只能用于Big Five(下面代码有),=delete可以用于任何函数身上(=0只能用于virturl函数)

      class Foo
      {
      public:
          // 1.普通构造函数
          Foo(int i):_i(i){}
          Foo() = default;// ctor可以多个并存
          // 2. 拷贝构造
          Foo(const Foo& x):_i(x._i){}
          Foo(const Foo&) = default;// error,不可重载
          Foo(const Foo&) = delete;// error,不可重载
          // 3. 拷贝赋值
          Foo& operator=(const Foo& x){_i = x.i; return *this;}
          Foo& operator=(const Foo& x) = default;// error,不可重载
          Foo& operator=(const Foo& x) = delete;// error,不可重载
          
          // 4. 普通函数
          void func1() = default;// error,普通函数没有默认值
          void func2() = delete;
          
          // 5. 析构函数
          ~Foo() = delete;// 
          ~Foo() = default;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
    17. 空类使用时,编译器会给予默认的版本,通常是public且inline的

      // 空类
      class Empty{};
      // 默认构造函数的显式声明
      class Empty
      {
      public:
          Empty(){}
          Empty(const Empty& rhs){}
      	~Empty(){}
          Empty& operator=(const Empty& rhs){}
      }; 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    18. 如果类内含有指针成员,那么通常需要写出Big-three出来

    19. 不可拷贝的类:将拷贝构造和拷贝赋值函数设置为delete

      struct NoCopy{
          NoCopy() = default;
          NoCopy(const NoCopy&) = delete;
          NoCopy &operator = (const NoCopy&) = delete;
          ~NoCopy() = default;
      };
      
      // 仅仅向友元开放
      class PrivateCopy{
      private:
          PrivateCopy(const PrivateCopy&);
          PrivateCopy &operator=(const PrivateCopy&);
      public:
          PrivateCopy() = default;
          ~PrivateCopy();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    20. 长生对象:对象的析构函数不可被调用,即对象创建后无法被终结

      struct NoDotor{
          NoDtor() = default;
          ~NoDtor() = delete;
      };
      NoDtor nd;// 在作用域内有效,但是超出对象的作用域,销毁对象时调用析构函数会报错
      NoDtor *p = new NoDtor();
      delete p;// error
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    21. Alias Template化名模板:比起宏来更加灵活,可以指定模板参数

      template
      using Vec = std::vector>;
      Vec coll;
      // typedef std::vector> Vec;
      
      • 1
      • 2
      • 3
      • 4
    22. 越简洁越不容易出错,少打字就少出错

    23. 通过迭代器萃取对象的类型

      template
      void test_moveable(Container c){
          typedef typename iterator_traits::value_type Valtype;
          for(long i = 0; i < SIZE; ++i)
              c.insert(c.end(), Valtype());
          output_static_data(*(c.begin()));
          Container c1(c);
          Container c2(std::move(c));
          c1.swap(c2);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    24. Template template parameter模板模板参数

      需要使用化名模板

    25. Type Alias类型化名

      // 下面两种使用的方式等价
      typedef void(*func)(int,int);// c语言形式
      using func = void(*)(int, int);// c++形式
      
      void example(int,int){}
      func fn = example;
      
      // 类内的使用
      template
      struct Container{
          using value_type = T;// typedef T value_type
      };
      template
      void fn2(const Cntr& c){
          typename Cntr::value_type n;
      }
      // 化名和typedef在本质上没有不同
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    26. using

      • using命令:using namespace std;
      • using声明:使用具体的其他成员using _base::variable
      • 化名
    27. noexcept:保证不丢异常

      void foo()noexcept;// 等价于void foo()noexcept(true);
      // noexcept(条件)放在函数后面,满足条件时表示,保证不丢异常
      
      • 1
      • 2
    28. 函数调用发生异常,如果没有处理就会向调用者传递。异常一定要被处理,如果没有处理会向源头传递,如果最终没有处理,std::terminate() 会调用std::abort() 进行程序中断

    29. 右值引用的构造函数必须使用noexcept,才能被vector成长的过程调用

      class MyString{
      private:
          char* _data;
          size_t _len;
      ···
      public:
          // move constructor
          MyString(MyString&& str)noexcept
              :_data(str._data), _len(str._len){···}
          
          //move assignment
          MyString& operator=(MyString&& str)noexcept
          {···return *this}
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    30. override改写/复写:需要函数签名(函数名和参数)完全相同,编译器会检查是否能改写

      struct Base{
          virtual void vfunc(float){}
      };
      
      struct Derived:Base{
          // error,已标注override,但是函数签名错误,编译器检查失败
          virtural void vfunc(int)override{}
      } 
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    31. final关键字

      // 写在类名后,表示该类无法被继承
      struct Base final{}
      struct Derived:Base{};// error,无法继承
      
      // 写在虚函数后,表示该虚函数不可被重写
      struct Base{
          virtual void f() final;
      };
      struct Derived:Base{
          void f();// error
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    32. decltype关键字(类似typeof)

      // 1. 用来声明未知返回类型(告诉编译器进行辅助推导)
      template 
      decltype(x+y) add(T1 x, T2 y);
      // 2. 适用于模板元编程
      typedef typename decltype(obj)::iterator iType;// 提取模板类对象的类型
      // 3. 获取匿名函数类型
      auto cmp = [](const Person& p1, const Person& p2){
          return p1.lastneme() < p2.lastname()||
              (	p1.lastname() == p2.lastname()&&
              	pq.firstname() < p2.firstname());
      };
      std::set  coll(cmp);
      // 获取容器类型并声明变量
      map coll;
      ···
      decltype(coll)::value_type elem;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    33. Lambdas关键字:可以用来定义内联函数和作为参数或本地对象

      // 语法
      auto 匿名函数名称 = [已有变量或引用](调用时传的参数){
          // 函数功能,外部参数在匿名函数内是副本
      }
      
      // 用于短函数功能的定义
      auto I = []{// 匿名函数的定义
          std::cout << "hello lambda" << std::endl;
      };
      ···
      I(); // 匿名函数的调用
      
      // 传值(变量可变必须加mutable)
      int id = 0;
      auto f = [id]()mutable{id++;};
      // 传引用
      int id = 0;
      auto f = [&id](int param){id++; ++param;};
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    34. Variadic Templates

      // 基本语法
      template 
      void func(const T& firstArg, const Types&... args){
          // 处理firstArg
          func(args...);
      }
      
      // 递归示例:第一个更特化
      template
      void printX(const T& firstArgs, const Types&... args){
          cout << firstArgs << endl;
          printX(args...);
      }
      
      template
      void printX(const Types&...args){
          
      }
      print(7.5, "hello", bitset<16>(377), 42);
      
      // C++模拟printf函数
      template
      void printf(const char *s, T value, Args... args){
          while(*s){
              if(*s == '%'&&*(++s) != '%'){
                  std::cout << value;
                  printf(++s, args...);
                  return ;
              }
              std::cout << *s++; 
          }
          throw std::logic_error("extra arguments provided to printf");
      }
      
      // 利用比较两个参数大小的max函数和参数模板进行
      int maximum(int n){
          return n;
      }
      template 
      int maximum(int n, Args... args){
          return std::max(n, maximum(args...));
      }
      
      // 首位操作方式不同 :
      // 1.使用tuple将任意元素组合成一包 2.使用sizeof...()获取元素个数
      
      
      • 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
    // tuple的递归继承
    template class tuple;
        template<> class tuple<>{};
        template
        class tuple
            :private tuple
        {
            typedef typle inherited;
         protected:
             Head m_head;
         public:
            tuple(){}
            tuple(Head v, Tail... vtail)
                :m_head(v), inherited(vtail...){}
            Head head(){return m_head;}
            inherited& tail(){return *this;}
        };
        ```
        
    35. 子类的对象具有父类的成分
    
    ### 第二讲 标准库
    
    1. Rvalue references右值引用:可以帮助解决非必要的copy,当拷贝的来源是一个右值,可以不必重新分配,而是使用该右值的引用
    
    2. 右值可以认为是一个临时对象,对该临时对象的引用可以看作右值引用,右值被引用后不能再被使用了,只能在GCC2.0之后使用
    
    3. 右值引用示例
    
       ```c++
       void process(int &i){// 左值传参
           cout<< "process(int &&):"<< i <
    • 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
    1. move(偷值):特别是对大的容器的引用,可以更加节省再重新拷贝的时间,但是原来的引用无法使用

    2. 尽量使用浅拷贝的引用,可以充分提高效率

    新增加容器
    1. 容器array

      template
      struct array{
          typedef _Tp		value_type;
          typedef _Tp*	pointer;
          typedef value_type*	iterator;
          
          // 支持0大小的数组声明
          value_type _M_instance[_Nm ? _Nm : 1];
          iterator begin(){
              return iterator(&_M_instance[0]);
          }
          iterator end(){
              return iterator(&_M_instance[_Nm]); 
          }
          ···
          // 没有ctor和dtor
      }
      
      // 数组声明
      int a[100];
      // 数组类型简化
      typedef int T[100];
      T c;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
    2. 容器hashtable

      • 常用于unorder容器的基础容器
      • 链式元素多于数组元素就进行扩容,数组元素个数增加两倍并重新进行分配
      • hash function进行元素的哈希运算获得hash code进行散列
      // G4.9  hash Function的调用获得一个随机数
      void *pi = (void)(new int(100));
      cout << hash()(123) << endl; // 整性的值hash code和原值相同
      cout << hash()(123L) << endl;
      cout << hash()(string("Ace")) << endl;// G2.9没有提供
      cout << hash()(pi) << endl;
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    3. tuple用例

      tuple> t;// complex是双倍类型空间
      cout << "sizeof=" << sizeof(t) << endl;
      tuple t1(41, 6.4, "nico");
      cout << "tuple, sizeof = " << sizeof(t1) << endl;
       
      cout << "t1:" << get<0>(t1) << '' << get<1>(t1) << '' << get<2>(t1);
      auto t2 = make_tuple(22, 44, "stacy");
      get<1>(t1) = get<1>(t2);// tuple的赋值,但是必须类型相同
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
  • 相关阅读:
    简单对比Java、Python、Go、Rust等常见语言计算斐波拉契数的性能
    基于单片机(STM32F103ZE)的智能家居集成控制器设计
    sublime删除特定内容所在行
    学习笔记-数据结构-线性表(2024-04-21)
    聚观早报 |三星将在印度生产5G设备;马斯克邀请盖茨开特斯拉Semi
    MyBatis-Plus QueryWrapper及LambdaQueryWrapper的使用
    springboot项目中获取业务功能的导入数据模板文件
    Node学习十五 —— 使用TLS/SSL提高安全性
    No module named ‘haystack.urls‘等各种django报错解决方案
    Linux 本地RStudio 工具安装&远程访问
  • 原文地址:https://blog.csdn.net/qq_43840665/article/details/126447874