现代c++(2)

本文概述了C++11引入的关键特性,如初始化块、限定作用域枚举、final关键字、override和=default等,展示了如何提升代码可读性和编译时检查。此外,还介绍了auto、range-based循环、结构化绑定等高级特性,以及STL容器的新方法,如emplace和try_emplace。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现代c++ 新特性

  • C++11标准中,我们可以使用花括号(即{})对任意类型的变量,无论是局部变量还是类变量进行初始化,而且不用是static类型。
    • *统一的类成员初始化语法 std::initializer_list
  • enumerator
      enum class Color{
          black,
          white,
          yellow
      }
    
    • 由于枚举值white对外部不可见(必须通过Color::white引用),所以可以定义一个同名的white变量。这种枚举变量被称为限定作用域的枚举。
  • C++11新增的一组非常具有标记意义的关键字和语法
    • final

      • final 关键字修饰一个类,这个类将不允许被继承
        class A final {
        };
        
    • override
      C++语法规定,在父类中加了virtual关键字的方法可以被子类重写,子类重写该方法时可以加或不加virtual关键字,
      可能会带来以下两个问题。

      • 当我们阅读代码时,无论子类重写的方法是否添加了virtual关键字,我们都无法直观地确定该方法是否是重写的父类方法。

      • 如果我们在子类中不小心写错了需要重写的方法的函数签名(可能是参数类型、个数或返回值类型),这个方法就会变成一个独立的方法,这可能会违背我们重写父类某个方法的初衷,而编译器在编译时并不会检查到这个错误。

      • 子类方法被 override 关键字修饰,表明该方法重写了父类的同名方法,加了该关键字后,编译器会在编译阶段做相应的检查,如果其父类不存在相同签名格式的类方法,编译器就会给出相应的错误提示。

          正确使用方法
          class A {
            protected:
              virtual void func( int k, int d)
              {
        
              }
          };
           class B :A {
             protected:
              virtual void func(int k, int d) override{
                
              }
           }
        
    • =default

      • 如果一个 C++类没有显式给出构造函数、析构函数、拷贝构造函数、operator=这几类函数的实现,则在需要它们时,编译器会自动生成;或者,在给出这些函数的声明时,如果没有给出其实现,则编译器在链接时会报错。如果使用=default标记这类函数,则编译器会给出默认的实现。
      • =default 最大的作用可能是在开发中简化了构造函数中没有实际初始化代码,“尤其是声明和实现分别属于.h和.cpp文件”
          使用方法
          //a.h
          class A {
            public:
              A() =default();
              ~A()=default();
          };
          //a.cpp
          #include "a.h"
          //不用再写构造函数和析构函数的实现了
        
    • =delete

      • 既然有强制让编译器生成构造函数、析构函数、拷贝构造函数、operator=的语法,那么也应该有禁止编译器生成这些函数的语法,没错,就是=delete。
        在C++98/03规范中,如果我们想让一个类不能被拷贝(即不能调用其拷贝构造函数),则可以将其拷贝构造函数和operator=函数定义成private

        使用方法
        class A {
          public:
            A() =default();
            ~A()=default();
          public:
            A(const A& a)= delete;
            A& operator =(const A& a)=delete;
        };
        
    • auto

      • C++11新标准中修改了其用法,让编译器自己推导一些变量的数据类型,auto 一般用于让编译器自动推导一些复杂的模板数据类型,以简化语法

          std::map<std::string,std::string> sessons;
          session["first"]="001";
          for (std::map<std::string,std::string>::iterator iter = session.begin();iter!=session.end();iter++)
          {
            std::cout<<iter->second<<std::endl;
          }
          for (auto iter = session.begin();iter!=session.end();iter++)
          {
            std::cout<<iter->second<<std::endl;
          }
        
    • Range-based循环语法

      • 在C++98/03规范中,对于一个数组int arr[10],如果我们想要遍历这个数组,则只能使用递增的计数去引用数组中的每个元素for(int i = 0; i<10;i++){}

      • 在C++11规范中有了for-each语法,可以这么写:

          int arr[10]={0};
          for (int i: arr)
          {
            std::cout<<arr[i]<<std::endl;
          }
          std::map<std::string,std::string> sessons;
          session["first"]="001";
          for (auto iter: sessons)
          {
            std::cout<<iter.second<<std::endl;
          }
        
      • for-each 语法中,iter 与容器中元素的数据类型(std::pair<std::string,std::string>)相同,因此使用iter.second可直接引用键值。

      • for-each 语法中,对于复杂的数据类型,迭代器是原始数据的拷贝,而不是原始数据的引用。

        • 解决方法 `for (auto& iter: sessons)
      • 自定义对象支持for-each语法:

        • 至少实现 Iterator begin();Iterrator end();
    • 结构化绑定

      • 与定义结构体相比,无论是通过std::pair的first、second还是std::tuple的std::get方法来获取元素子属性,这些代码都是难以维护的,其根本原因是 first和second这样的命名不能做到见名知意。
      • 结构化绑定语法
      auto [a,b,c] = expression;
      auto [a,b,c] {expression};
      auto [a,b,c...](expression);
      
      ep1:
         auto [iterator,inserted] = someMap.insert(...);
      ep2:
        double myArray[3]={1.0,2.0,3.0};
        auto[a,b,c]=myArray;
      ep3:
        struct Point{
          double x;
          double y;
        };
        Point myPoint(10.0,20.0);
        auto[myX,myY]= myPoint;
        const auto& [myX,myY]= myPoint;
      
      • 当绑定类型不是基础数据类型时,如果你的本意不是想要得到绑定目标的副本,则为了避免拷贝带来的不必要开销,建议使用引用;如果不需要修改绑定目标,则建议使用const引用。
          std::map<std::string,int> cities;
          cities["beijing"]=0;
          for (const auto&[city,cityNum] : cities)
          {
            std::cout<<city<<cityNum<<std::endl;
          }
      
      • 用于结构化绑定的变量不能使用constexpr修饰或声明为static
    • stl容器新增的实用方法

      • 原位构造元素 emplace和emplace_back

        • 在一个循环里产生一个对象,然后将这个对象放入集合中,这样的代码在实际开发中太常见了。但是这样的代码存在严重的效率问题:循环中的t对象在每次循环时,都分别调用了一次构造函数、拷贝构造函数和析构函数,但实际上,我们的初衷是创建一个对象t,将其直接放入集合中,而不是将t作为一个中间临时产生的对象,调用t的构造函数1次就可以了。C++11提供了一个在这种情形下替代 push_back 的方法——emplace_back。通过使用emplace_back,可以将main函数中的代码改写如下:
        
         std::list<Test> testList;
         for (int i=0;i<10;++i)
         {
           // Test t(1*i,2*i,3*i);
           // testList.push_back_back(t);
           testList.emplace_back(1*i,2*i,3*i);
         }
        

        经过以上改写,在实际执行时只需调用Test类的构造函数10次,大大提高了执行效率。

      • c++17 std::map的try_emplace方法与insert_or_assign方法

        std::map<std::string,int> mapUsersAge{{"aa",1},{"bb",2}};
        mapUsersAge.try_emplace("TOM",26);
        mapUsersAge.insert_or_assign("TOM",26);
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值