C++11新特性

C++11新特性

  1. C++03标准中,__cplusplus的值被预定为$199711L$,在C++11标准中,__cplusplus的值被预定为$201103L$,程序判断是否支持C++11(有些编译器是部分支持):
1
2
3
#if __cplusplus < 201103L
#error "should use C++11 implementation"
#endif
  1. 尽量用assert(n>0)进行断言,程序发布时加上宏定义值NDEBUG把断言禁掉,调试的时候去掉NDEBUG
  2. lambda函数语法定义:(参数列表和返回类型都是可选的部分,捕捉列表和函数体都可能为空。最简略的lambda函数为:[]{};
1
[capture](parameters)mutable->return-type{statement}
  • [capture]:捕捉列表,有以下几种:

    [var]表示值传递方式捕捉变量var

    [=]表示值传递方式捕捉所有父作用域的变量(包括this

    [&var]表示引用传递捕捉变量var

    [&]表示引用传递捕捉所有父作用域的变量(包括this

    [this]表示值传递方式捕捉当前的this指针

    ​ 值传递与引用传递的区别:按值方式传递的捕捉列表,其传递的值在lambda函数定义的时候就已经决定了。按引用传递的捕捉列表变量,其传递的值等于lambda函数调用时的值,举例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include<iostream>
    using namespace std;

    int main()
    {
    int j=12;
    auto by_val_lambda=[=]{return j+1;};
    auto by_ref_lambda=[&]{return j+1;};
    cout<<"by_val_lambda:"<<by_val_lambda()<<endl;
    cout<<"by_ref_lambda:"<<by_ref_lambda()<<endl;
    j++;
    cout<<"by_val_lambda:"<<by_val_lambda()<<endl;
    cout<<"by_ref_lambda:"<<by_ref_lambda()<<endl;
    }

    运行结果:

    1
    2
    3
    4
    by_val_lambda:13
    by_ref_lambda:13
    by_val_lambda:13
    by_ref_lambda:14
  • (parameters):参数列表。

    与普通函数的参数列表一致。如果不需要参数传递,则可以连同()一起省略

  • mutable:修饰符。

    默认情况下,lambda函数总是一个const函数,mutable可以取消常数特性。

    在使用该修饰符时,参数列表不可省略(即使参数为空)

  • ->return-type:返回类型。

    不需要返回值的时候可以连同符号->一起省略。

    在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。

  • {statement}:函数体。

    内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕捉的变量。

  1. 继承

    • 关键字final:阻止继承,声明时使用的话,子类无法再重栽该虚函数;

    • 关键字override:重栽父类的虚函数时,在声明函数时进行修饰,可以在编译时检查函数名和参数列表;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Object{
    virtual void fun()=0;
    virtual void output()=0;
    };

    class Base:public Object{
    void fun() final; // 声明为final
    void output()override; //如果名字写错或者参数写错,编译出错,主要用来明确多态
    };

    class Derived:public Base{
    void fun(); // 无法通过编译
    };
  2. C++11支持using关键字,可以直接使用基类的构造函数,之前using只能使用基类的普通函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Base {
    public:
    Base(int i) {cout<<"Base construct i:"<<i<<endl;}
    Base(int i,int j) { cout << "Base construct i:" << i<<" j:"<<j << endl; }
    virtual ~Base() {}
    void f(double i) { cout << "Base:" << i << endl; }
    };

    class Derived :public Base{
    public:
    using Base::Base;
    using Base::f;
    void f(int i,int j) { cout << "Derived:" << i << endl; }
    };

    int main()
    {
    Base b;
    b.f(4.5);
    Derived d1(5),d2(5,6);//这里自动调用基类的构造函数
    d1.f(4.5);//如果没有using base::f会产生编译错误
    d1.f(5, 6);
    }

    输出结果为:

    1
    2
    3
    4
    Base construct i:5
    Base construct i:5 j:6
    Base:4.5
    Derived:5
  3. 移动构造函数

    移动构造函数接受一个所谓的“右值引用”的参数(右值可以理解为临时变量的引用),

    然后将临时变量的数据指针转移给本对象成员,随后将临时变量的数据指针置空。

    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
    class HasPtrMem
    {
    public:
    HasPtrMem() :d(new int(0)) {
    cout << "Construct:" << ++n_cstr << endl;
    }
    HasPtrMem(const HasPtrMem& h) :d(new int(*h.d)) {
    cout << "Copy construct:" << ++n_cptr << endl;
    }
    HasPtrMem(HasPtrMem&& h) :d(h.d) { //移动构造函数
    h.d = nullptr; //将临时值的指针成员置空
    cout << "Move construct:" << ++n_mvtr << endl;
    }
    ~HasPtrMem() {
    delete d;
    cout << "Destruct:" << ++n_dstr << endl;
    }

    public:
    int* d;
    static int n_cstr;
    static int n_dstr;
    static int n_cptr;
    static int n_mvtr;
    };

    int HasPtrMem::n_cstr = 0;
    int HasPtrMem::n_dstr = 0;
    int HasPtrMem::n_cptr = 0;
    int HasPtrMem::n_mvtr = 0;

    HasPtrMem GetTemp() {
    HasPtrMem h;
    cout << "Resource from " << __func__ << ":" << hex << h.d << endl;
    return h;
    }

    int main()
    {
    HasPtrMem a = GetTemp();
    cout << "Resource from " << __func__ << ":" << hex << a.d << endl;
    }

    输出结果:

    1
    2
    3
    4
    5
    6
    7
    8
    Construct:1    //函数GetTemp()中变量h构造函数
    Resource from GetTemp:0x603010 //函数GetTemp()第二句
    Move construct:1 //函数GetTemp()第三句返回时会产生临时对象,调用移动构造函数(如果没有移动构造函数,将会调用拷贝构造函数,开销比较大)
    Destruct:1 //函数GetTemp()中h变量析构
    Move construct:2 //临时变量给main()函数中的a赋值,调用移动构造函数,同上
    Destruct:2 //临时变量析构
    Resource from main:0x603010 //调用main()函数中第二句
    Destruct:3 //main()函数中变量a析构
  4. auto作为类型指示符,以下几种情况不能用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void fun(auto x=1){}    //1、auto函数参数,无法通过编译

    struct str{
    auto var=10; //2、auto非静态成员变量,无法通过编译
    };

    int main(){
    char x[3];
    auto y=x;
    auto z[3]=x; //3、auto数组,无法通过编译

    //4、auto模板参数(实例化时),无法通过编译
    vector<auto> v={1};
    }

    (1)对于函数fun来说,auto不能是其形参类型。如果需要泛型的参数,需要用使用模板;

    (2)非静态成员变量的类型不能是auto;即使结构体的成员有初始值,编译器也不会对其进行推导,;

    (3)编译器禁止声明auto数组;

    (4)实例化模板不能使用auto作为模板参数;

    以上四种情况基本相似,虽然看起来很容易能推导出auto对应的类型,但现在的C++11还不支持这样的使用方式。

  5. decltype的应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main(){
    vector<int> vec;
    typedef decltype(vec.begin()) vectype;
    for(vectype i=vec.begin();i<vec.end();i++){
    //dosomething
    }
    for(decltype(vec)::iterator i=vec.begin();i<vec.end();i++){
    //dosomething
    }
    }
  6. 遍历容器

    1
    2
    3
    4
    5
    6
    7
    8
    int main(){
    {
    int arr[5]={1,2,3,4,5};
    for(int& e:arr) //需要修改项用引用,这里int可用auto代替
    e*=2;
    for(int e:arr) //不需要修改项直接用变量,这里int可用auto代替
    cout<<e<<'\t';
    }
  7. for_eachtransform

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ///将容器a中的元素求log后放入a中
    std::transform(a.begin(),a.end(),a.begin(),[](double d)->double{return log(d);});
    std::for_each(a.begin(),a.end(),[](double& d){d = log(d);});

    ///两个vector<double>V,W要进行某个运算,如计算U = sin(V) + W,结果存入U;
    std::transform(v.begin(),v.end (),w.begin (),u.begin (),[&](double a ,double b)->double{
    return sin(a)+b;
    });

    ///使用std::transform可以比较方便的实现序列容器的四则运算
    stl提供了plus,multiplies,divides,modulus,negate等函数对象,方便实现加减乘除等运算
    std::transform(a.begin(),a.end(),b.begin(),std::back_inserter(c),std::plus<double>());
  8. 原子类型(多线程时不用加锁)

    使用atomic类模板自定义类型:std::atomic<T> t;

    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
    #include <atomic>
    #include <thread>

    atomic<int> a;
    atomic<int> b;

    int Thread1(int) {
    int t = 1;
    a.store(t, memory_order_relaxed);
    b.store(2, memory_order_release);// 本原子操作前所有的写原子操作必须完成
    return 0;
    }

    int Thread2(int) {
    while (b.load(memory_order_acq_rel) != 2); // 本原子操作必须完成才能执行之后所有的读原子操作
    cout << a.load(memory_order_relaxed) << endl;
    return 0;
    }

    int main()
    {
    thread t1(Thread1, 0);
    thread t2(Thread2, 0);
    t1.join();
    t2.join();
    return 0;
    }
  9. defaultdelete

    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
    class Base{
    public:
    Base(int i){}
    Base()=default; ///显式声明系统提供的缺省构造函数
    Base(const Base& rb)=delete; ///显式删除系统提供的缺省拷贝构造函数
    };

    bool isLucky(int number);

    int main()
    {
    isLucky('a'); ///存在隐式转换
    isLucky(true); ///存在隐式转换
    isLucky(3.5); ///存在隐式转换
    }

    ///增加删除不想要的重载函数
    bool isLucky(char)=delete;
    bool isLucky(bool)=delete;
    bool isLucky(double)=delete;

    int main()
    {
    isLucky('a'); ///错误!调用删除函数
    isLucky(true); ///错误!
    isLucky(3.5); ///错误!
    }
  10. 枚举类型增加作用域限制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    int main()
    {
    enum Color{
    black,
    white,
    red
    };
    auto white=false; //编译提示变量重定义
    }

    int main()
    {
    enum class Color:uint32_t{ //可以指定枚举值得类型,不指定时编译器会自动指定
    black,
    white,
    red
    };
    auto white=false; ///编译OK
    Color c = white; ///编译错误,提示当前作用域没有这个枚举元素
    Color c = Color::white; ///编译OK
    }
  11. 智能指针std::unique_ptrstd::shared_ptr的区别

    共同之处:是开发人员不再需要关心指针所指内存的释放问题,当这块内存不在被引用时,会被自动释放。

    区别:指向同一内存的某一类型指针的数量,std::unique_ptr只允许一个,而std::shared_ptr允许多个。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    std::unique_ptr ///最多允许一个,当这个智能指针被销毁时,对应的内存会被自动释放。
    ///如果试图去拷贝一个std::unique_ptr,编译器会报错,例如下面的代码就会报错
    std::unique_ptr<Base> p(new Base); // OK
    std::unique_ptr<Base> p1 = p; // 编译错误,不能拷贝std::unique_ptr

    ///std::unique_ptr支持move语法,所以可以这样
    std::unique_ptr<Base> p(new Base); // OK
    std::unique_ptr<Base> p1 = std::move(p); // OK

    ///对于std::shared_ptr,上面的拷贝的那个例子就OK,

    std::shared_ptr<Base> p(new Base); // OK
    std::shared_ptr<Base> p1 = p; // OK