C++PrimerPlus学习之探讨C++新标准

  • 复习前面介绍过的C++11功能
    • decltype
      • 关键字decltype将变量的类型声明为表达式指定的类型。

      • 示例

        double x;
        int n;
        decltype(x*n)q;//q same type as x*n,double.
        decltype(&x)pd;//pd same as &x,double *
        
      • 返回类型后置,能够使用decltype来指定函数的返回类型

        template<typename T,typename U>
        auto eff(T t,U u)->decltype(T*U)
        {
        	...
        }
        
    • 模板别名:using=
      • 对于冗长或复杂的标识符,可以用typedefusing=
        typedef std::vector<std::string>::iterator itType;
        using itType=std::vector<std::string>::iterator;
        
      • 可用于模板部分具体化
        template<typename T>
        	using arr12=std::array<T,12>;
        	arr12<double>a1;
        	arr12<string>a2;
        
    • nullptr
      • C++11新增关键字,用于表示空指针;它是指针类型,不能转换为整型类型。
      • 可以向后兼容,C++11仍允许使用0来表示空指针,因此表达式nullptr==0true
    • 显示转换运算符explicit
      • 禁止单参数构造函数和转换函数的自动转换

        class Plebe
        {
        	Plebe(int);
        	explicit Plebe(double);
        	operator int()const;
        	explicit operator double()const;
        	...
        };
        Plebe a,b;
        a=5;//allowed
        b=0.5;//not allowed
        b=Plebe(0.5);//allowed
        //
        int n=a;//allowed
        double x=b;//not allowed
        double x=double(b);//allowed
        

移动语义和右值引用

  • 将右值关联到右值引用导致该右值被存储到特定的位置,且可以获取该位置的地址。

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        double a=12.4;
        double &&p1=7.07;
        double &&p2=12*a+3.4;
        cout<<p1<<' '<<&p1<<endl;
        cout<<p2<<' '<<&p2<<endl;
    }
    /*
    output:
    7.07 0x6afee0
    152.2 0x6afee8
    */
    
  • 为何需要移动语义

    • 减少从临时变量复制数据的步骤,提高效率
  • 移动构造函数可能修改其实参,故右值引用参数不应是const

  • 在引入右值引用前,左值引用不能指向右值,那么这时候会产生一个临时变量。

  • 如果想对一个左值来使用移动构造函数,可以使用move(),其头文件为utility

    • 示例

      class ABC{...}
      ABC a;
      ABC b;
      b=std::move(a);
      
    • 如果ABC没有定义移动赋值运算符,编译器将使用复制赋值运算符。如果也没有定义复制赋值运算符,将根本不允许上述赋值。一般情况下这些都是默认的成员函数,故一般都可以允许赋值。

新的类功能

  • 如果提供了复制构造函数或复制赋值运算符,编译器将不会自动提供移动构造函数和移动赋值运算符;如果提供了移动构造函数或移动赋值运算符,编译器将不会自动提供复制构造函数和复制赋值运算符。
  • 默认的方法和禁用的方法
    • 假定要使用某个默认的函数,而这个函数由于某种原因不会自动创建。例如,您提供了移动构造函数,因此编译器不会自动创建默认的构造函数,复制构造函数和复制赋值构造函数。在这些情况下,可以使用关键字default显式地声明这些方法的默认版本:

      class Someclass
      {
      public:
          Someclass(Someclass &&);
          Someclass()=default;
          Someclass(const Someclass &)=default;
          Someclass &operator=(const Someclass &)=default;
          ...
      };
      
    • 另一方面,如果要禁止编译器使用特定方法,可使用关键字delete,例如禁止复制对象,可禁用复制构造函数和复制赋值运算符:

      class Someclass
      {
      public:
          Someclass(Someclass &&)=default;
          Someclass &operator=(Someclass &&)=default;
          Someclass()=default;
          Someclass(const Someclass &)=delete;
          Someclass &operator=(const Someclass &)=delete;
          ...
      };
      
    • 要禁止复制,可将复制构造函数和赋值运算符放在类定义的private部分,但使用delete也能达到这个目的,且更不容易犯错,更容易理解。

    • 关键字default只能用于6个特殊成员函数,但delete可用于任何成员函数。delete的一种可能用法是禁止特定的转换。

  • 管理虚方法:overridefinal
    • 如果基类声明了一个虚方法,在派生类提供不同的版本将会覆盖旧版本,但如果特征标不匹配,那么就会将隐藏而不是覆盖就旧版本:

      class ABC
      {
      public:
          virtual void show(int a)const{cout<<a<<endl;}
      };
      class P:public ABC
      {
      public:
          virtual void show()const{cout<<"hello world"<<endl;}
      };
      int main()
      {
          P tmp;
          tmp.show();//allowed
          tmp.show(1);//not allowed
      }
      
    • 使用虚说明符override指出一个要覆盖的虚函数,那么如果该虚函数在派生类声明与基类方法不匹配,编译器将视为错误。

    • 使用虚说明符final可禁止派生类覆盖特定的虚方法

      virtual void show(int a)const override{cout<<a<<endl;}//override
      virtual void show(int a)const final{cout<<a<<endl;}//final
      

Lambda函数

  • lambda表达式完全由一条语句组成时,自动类型推断才管用;否则,需要使用新增的返回型后置语法:

    [](int x){return x%3==0};
    [](double x)->double{int y=x;return x-y;}//return type is double
    
  • 为何使用lambda

    • 相比于函数,程序员可以较快了解该语句的功能,而不需要翻阅函数的定义

    • 相比于函数符类,它更加简洁明了

    • 它可以这样使用

      auto mod3=[](int x){return x%3==0;}
      count1=std::count_if(n1.begin(),n1.end(),mod3);
      count2=std::count_if(n2.begin(),n2.end(),mod3);
      bool result=mod3(3);
      
    • lambda可访问作用域内的任何动态变量:要捕获要使用的变量,可将其名称放在中括号内。如果只指定了变量名,如[z],将按值访问变量;如果在名称前加上&[&count],将按引用访问变量。[&]让你能够按引用访问所有动态变量,而[=]让你能够按值访问所有动态变量。还可以混合使用这两种方式,例如,[ted,&ed]让你能够按值访问ted以及按引用访问ed,[&,ted]让你能够按值访问ted以及按引用访问其他所有动态变量,[=,&ed]让你能够按引用访问ed以及按值访问其他所有动态变量。

    • 一个例子

      #include<bits/stdc++.h>
      using namespace std;
      int main()
      {
          int times=390000;
          srand(time(0));
          int count3=0,count13=0;
          auto mod3=[&count3](int x){count3+=x%3==0;};
          auto mod13=[&count13](int x){count13+=x%13==0;};
          vector<int>V(times);
          generate(V.begin(),V.end(),rand);
          for_each(V.begin(),V.end(),mod3);
          for_each(V.begin(),V.end(),mod13);
          cout<<count3<<' '<<1.0*count3/times<<endl;
          cout<<count13<<' '<<1.0*count13/times<<endl;
      }
      

包装器

  • C++提供了多个包装器(wrapper,也叫适配器[adapter]).这些对象用于给其他编程接口提供更一致或更合适的接口。如bind1stbind2nd

  • 当模板参数为函数名,函数指针,函数对象,或有名称的lambda表达式时,会导致模板的效率极低。这时则需要利用function从调用特征标的角度定义一个对象,使得模板函数适用于特征标相同的函数指针,函数对象或lambda表达式。

  • function的使用示例

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

    它接受一个char参数和一个int参数,并返回一个double值。那么我们可以将特征标与之相同的任何函数指针,函数对象或lambda表达式可以赋给它。

  • 一个例子

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T,typename F>
    T use_f(T v,F f)
    {
        static int count=0;
        count++;
        cout<<count<<' '<<&count<<' ';
        return f(v);
    }
    class Fp
    {
    private:
        double z;
    public:
        Fp(double _z):z(_z){}
        Fp(){}
        double operator()(double p){return z*p;}
    };
    double dub(double x){return 2.0*x;}
    int main()
    {
        auto f1=[](double x){return x+10.4;};
        cout<<use_f(3,f1)<<endl;
        cout<<use_f(4,dub)<<endl;
        cout<<use_f(5,Fp(2.3))<<endl;
        cout<<endl<<"after use function()"<<endl;
        function<double(double)>F1=f1;
        function<double(double)>F2=dub;
        function<double(double)>F3=Fp(2.3);
        cout<<use_f(3,F1)<<endl;
        cout<<use_f(4,F2)<<endl;
        cout<<use_f(5,F3)<<endl;
    }
    /*
    output:
    1 0x48c00c 13
    1 0x480254 8
    1 0x480250 11
    
    after use function()
    1 0x480258 13
    2 0x480258 8
    3 0x480258 11
    */
    
    
  • 其他使用方式

    typedef function<double(double)>fdd;//simplify the type declaration
    cout<<use_f(y,fdd(dub))<<endl;
    ...
    //
    template<typename T>
    T use_f(T v,std::function<T(T)>f)
    {
    	...
    	return f(v);
    }
    cout<<use_f<double>(y,dub)<<endl;
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值