• C++11新特性


    在学习C++11前,请确定编辑器打开了C11标准!

    int main(){
    	cout<<__cplusplus<<endl;//输出201103而不是别的
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4

    头文件的改变

    #include //新的头文件为
    #include//兼容旧的写法
    
    • 1
    • 2

    新引入的nullptr

    在c++中如果表示空指针语义时建议使用nullptr而不要使用NULL,因为NULL本质上是个int型的0,其实不是个指针。
    
    • 1

    一、自动类型推导

    1、auto

    auto:在编译时期进行通过 = 右边的类型推导出变量类型

    // 10是int型,可以自动推导出a是int,使用 auto 类型推导的变量必须马上初始化,因为 auto 在 C++11 中只是“占位符”,并非如 int 一样的真正的类型声明。
    auto a = 10; 
    
    // &a 的结果是一个 int* 类型的指针,所以推导出变量 b 的类型是 int*
    auto b = &a; 
    
    // 双引号""围起的字符串是 const char* 类型,所以推导出变量 url 的类型是 const char*,即常量指针
    auto url = "http://c.biancheng.net/cplus/";
    
    // 当 = 右边的表达式是一个引用类型时,auto 会把引用抛弃,直接推导出它的原始类型。
    auto &c1  = a;   	//c1 为 int&,auto 推导为 int
    auto c2 = c1;    	//c2 为  int,auto 推导为 int
    
    //  auto 与 const 结合使用时,当类型不为引用时,auto 的推导结果将不保留表达式的 const 属性;
    const auto d = a;   // d是const int
    auto e = d; 		// e是int
    //  auto 与 const 结合使用时,当类型为引用时,auto 的推导结果将保留表达式的 const 属性。
    const auto& f = a;  // f是const int&
    auto &g = f; 		// g是const int&
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    auto的一些使用限制规则

    auto d = 0, f = 1.0; 	 // error,0和1.0类型不同,对于编译器有二义性,没法推导
    auto e; 				 // error,使用auto必须马上初始化,否则无法推导类型
    auto c[10] = a; 		 // error,auto不能定义数组,可以定义指针
    vector<auto> f = d;      // error,auto无法推导出模板参数
    
    void func(auto value) {} // error,auto不能用作函数参数
    
    class A {
        auto a = 1; 		 // error,在类中auto不能用作非静态成员变量
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    auto的使用场景

    // 一、简化迭代器的定义
    vector< vector<int> > v;
    vector< vector<int> >::iterator i = v.begin();
    auto i = v.begin();  //auto 可以根据begin() 函数的返回值类型来推导出变量 i 的类型
    
    
    // 二、泛型编程,用于不知道变量类型,不希望指明具体类型的时候
    #include 
    using namespace std;
    
    class A{
    public:
        static int get(void){
            return 100;
        }
    };
    
    class B{
    public:
        static const char* get(void){
            return "http://c.biancheng.net/cplus/";
        }
    };
    
    template <typename T>
    void func(void){
        auto val = T::get();
        cout << val << endl;
    }
    
    int main(void){
        func<A>();
        func<B>();
    
        return 0;
    }
    
    • 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

    2、decltype(declare type 声明类型)

    decltype:在编译时期根据表达式类型推导出变量类型

    auto 并不适用于所有的自动类型推导场景,在某些特殊情况下 auto 用起来非常不方便,甚至压根无法使用,所以 decltype 关键字也被引入到 C++11 中。

    decltype推导的规则

    1. 如果 exp 是一个不被括号( )包围的表达式,或者是一个类成员访问表达式,或者是一个单独的变量,那么 decltype(exp) 的类型就和 exp 一致,这是最普遍最常见的情况。
    // b是const int&,auto 要求变量必须初始化,而 decltype 不要求。
    int i = 1;
    decltype(i) b; //b被推导为int类型
    
    • 1
    • 2
    • 3
    1. 如果 exp 是函数调用,那么 decltype(exp) 的类型就和函数返回值的类型一致。
    decltype(exp) ;
    // exp 这个普通的表达式可以是任意复杂的形式,但是必须要保证 exp 的结果是有类型的,不能是 void;
    //例如,当 exp 调用一个返回值类型为 void 的函数时,exp 的结果也是 void 类型,此时就会导致编译错误。
    decltype(10.8) x = 5.5;  //x 被推导成了 double
    decltype(x + 100) y;  //y 被推导成了 double
    
    //函数声明
    int& func_int_r(int, char);  //返回值为 int&
    int&& func_int_rr(void);  //返回值为 int&&
    int func_int(double);  //返回值为 int
    const int& fun_cint_r(int, int, int);  //返回值为 const int&
    const int&& func_cint_rr(void);  //返回值为 const int&&
    
    //decltype类型推导,这里 exp 中调用函数时需要带上括号和参数,但这仅仅是形式,并不会真的去执行函数代码。
    int n = 100;
    decltype(func_int_r(100, 'A')) a = n;  //a 的类型为 int&
    decltype(func_int_rr()) b = 0;  //b 的类型为 int&&
    decltype(func_int(10.5)) c = 0;   //c 的类型为 int
    decltype(fun_cint_r(1,2,3))  x = n;    //x 的类型为 const int &
    decltype(func_cint_rr()) y = 0;  // y 的类型为 const int&&
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 如果 exp 是一个左值,或者被括号( )包围,那么 decltype(exp) 的类型就是 exp 的引用;假设 exp 的类型为 T,那么 decltype(exp) 的类型就是 T&。
    class Base{
    public:
        int x;
    };
    int main(){
        const Base obj;
        //带有括号的表达式
        decltype(obj.x) a = 0;  //obj.x 为类的成员访问表达式,符合推导规则一,a 的类型为 int
        decltype((obj.x)) b = a;  //obj.x 带有括号,符合推导规则三,b 的类型为 int&。
        //加法表达式
        int n = 0, m = 0;
        decltype(n + m) c = 0;  //n+m 得到一个右值,符合推导规则一,所以推导结果为 int
        decltype(n = n + m) d = c;  //n=n+m 得到一个左值,符号推导规则三,所以推导结果为 int&
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    关于decltype的使用

    auto 的语法格式比 decltype 简单,所以在一般的类型推导中,使用 auto 比使用 decltype 更加方便;
    但是在一些auto不方便使用的场景下,decltype会更加好用,以下举例

    1、auto推导必须初始化变量,而decltype只需要根据表达式即可,如果不想初始化变量即声明类型时;
    2、auto 只能用于类的静态成员,不能用于类的非静态成员(普通成员),如果我们想推导非静态成员的类型时(详细的一个例子:http://c.biancheng.net/view/7151.html)
    3、decltype 和 auto 结合起来完成返回值类型的推导(具体的例子http://c.biancheng.net/view/3727.html)

    二、左值右值

    详细的看这个–>左值引用、右值引用、移动语义、完美转发的所有 - - 程序喵大人

    • 左值:可以取地址并且有名字的东西就是左值。
    • 右值:不能取地址的没有名字的东西就是右值。
    • 纯右值:运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。
    • 将亡值:可以理解为即将要销毁的值。
    • 左值引用:对左值进行引用的类型。
    • 右值引用:对右值进行引用的类型。
    • 移动语义:转移资源所有权,类似于转让或者资源窃取的意思,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用。
    • 完美转发:可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参。
    • 返回值优化:当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。

    三、模板的改进

    1、模板的右尖括号
    C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,所以需要中间加个空格进行分割,避免发生编译错误。

    int main() {
       std::vector<std::vector<int>> a; // error
       std::vector<std::vector<int> > b; // ok
    }
    
    • 1
    • 2
    • 3
    • 4

    2、模板的别名(using代替typedef)

    C++11引入了using,可以轻松的定义别名且简洁易读

    typedef std::vector<std::vector<int>> vvi; // before c++11
    using vvi = std::vector<std::vector<int>>; // c++11
    
    • 1
    • 2

    因为:using 语法和 typedef 一样,并不会创造新的类型,
    所以:func_t 定义的 xx_2 并不是一个由类模板实例化后的类,而是 void(*)(int, int) 的别名。

    /* C++98/03 */
    template <typename T>
    struct func_t
    {
        typedef void (*type)(T, T);
    };
    // 使用 func_t 模板
    func_t<int>::type xx_1;
    
    
    /* C++11 */
    template <typename T>
    using func_t = void (*)(T, T);
    // 使用 func_t 模板
    func_t<int> xx_2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    除此之外,typedef定义函数指针简直太难读懂!使用using就好很多

    typedef void (*func)(int, int); // 啥玩意,看不懂
    using func = void (*)(int, int); // 起码比typedef容易看的懂吧
    
    • 1
    • 2

    3、函数模板的默认模板参数

    C++11之前只有类模板支持默认模板参数,函数模板是不支持默认模板参数的,C++11后都支持。

    template <typename T, typename U=int>
    class A {
        T value;  
    };
    
    template <typename T=int, typename U> // error
    class A {
        T value;  
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    且类模板的默认模板参数必须从右往左定义,而函数模板则没有这个限制。对于函数模板,参数的填充顺序是从左到右的。

    template <typename R, typename U=int>
    R func1(U val) {
       return val;
    }
    
    template <typename R=int, typename U>
    R func2(U val) {
       return val;
    }
    
    int main() {
       cout << func1<int, double>(99.9) << endl; // 99
       cout << func1<double, double>(99.9) << endl; // 99.9
       cout << func1<double>(99.9) << endl; // 99.9
       cout << func1<int>(99.9) << endl; // 99
       cout << func2<int, double>(99.9) << endl; // 99
       cout << func1<double, double>(99.9) << endl; // 99.9
       cout << func2<double>(99.9) << endl; // 99.9
       cout << func2<int>(99.9) << endl; // 99
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    参考文章:
    1. C++新特性汇总–腾讯云
    2. C++的auto–c语言中文网
    3. C++新特性汇总–知乎

    other奇葩小知识

    C++以下选项中那种变量类型没有布尔值?
    A 、int B、char C、void D、double
    答:void 是一种空类型,不可存储具体的值,因此没有布尔值

    在C++中,还可以使用 const 关键字来限制类的对象被复制。将类的拷贝构造函数和赋值运算符声明为 const 可以防止对象被复制,但允许对象进行移动语义的操作。此外,也可以将类的拷贝构造函数和赋值运算符声明为删除的,从而完全禁止对象的复制操作。

    在C++中,异常规格说明已被弃用,取而代之的是 noexcept 关键字。

    noexcept 是一个类型修饰符,用于指定函数是否会抛出异常。如果一个函数被声明为 noexcept,则它保证不会抛出任何类型的异常,包括其内部可能抛出的异常。如果函数确实抛出了异常,程序将调用 std::terminate() 终止程序执行。这有助于提高代码的可读性和可维护性,并允许编译器进行更好的优化。

    持续更新中…挖坑…

  • 相关阅读:
    【数据分享】2014-2022年我国淘宝村点位数据(Excel格式/Shp格式)
    springboot之Filter的URI匹配规则
    nn.functional.interpolate
    (免费领源码)java#SpringBoot#mysql客户信息管理系统80944-计算机毕业设计项目选题推荐
    反射和序列化操作会破坏单例模式
    JAVAWeb Session 会话概述与基本用法
    Toward Fast, Flexible, and Robust Low-Light Image Enhancement
    钉钉智慧校园小程序如何开发,你知道么!
    Python模块和包
    数据治理工程师(CDGA)学习心得分享
  • 原文地址:https://blog.csdn.net/BinBinCome/article/details/129346401