zoukankan      html  css  js  c++  java
  • C++11新特性

    本文主要总结深入理解c++11中的常用新特性

    新标准的诞生

    保证平稳性和兼容性

    通用为本,专用为末

    右值引用

    在没有右值引用时,对带有指针成员变量的对象进行拷贝,会让指针的内存变得很难管理,甚至会产生错误。这是如果有移动语义,对象不执行拷贝,只执行移动就不会就这种错误。

    在介绍移动语义之前,首先得明白左值,右值,和右值引用。

    一般而言,可以取地址,有名字的就是左值。反之,不能取地址,没有名字的就是右值。我们称c++98中的引用为“左值引用”,而c ++11中的引用为右值引用。左值引用是一个具名对象的别名,而右值引用是一个不具名对象的别名。

    右值引用延长了将亡对象的生命期。对应的,常量左值引用(const T &)也能延长一个将亡对象的生命期,但是付出的代价就是余生只能是只读的。

    非常量左值引用只能绑定非常量左值

    非常量右值引用只能绑定非常量右值

    常量左值引用可以绑定任何值

    常量右值引用没有用

    右值引用一般的编写方式如下:

    
        class Moveable{
        public:
            Moveable(const Moveable& m);
            Moveable& operate=(const Moveable&);
            Moveable(Moveable&& m);
            Moveable& operate=(const Moveable&&);
    

    std::move能够将一个对象的属性从左值变成右值,但是需要之一的是并不会改变对象的生命期。

    在编写模板的时候,我们经常需要实现完美转发。例如:

    
        template <typename T>
        void IamForwording(T&& t)
        {
            IrunCodeActually(forward(t));
        }
    

    使用forward可以实现完美转发,实际上,forward的功能和move一样,只不过在模板里面一般使用forward。它们都等于static_cast<T&&>,具体为什么可以去看“右值引用的模板推导”。

    列表初始化

    我觉得一般知道可以如下方式去初始化容器就够了。

    
        vector<int> a = {1, 2, 3, 4};
        map<int, float> b = {{1, 2.3f}, {2, 3.2f}};
    

    POD类型

    在c++11中,POD的概念被分为平凡和标准布局两个概念。平凡强调了类型没有复杂的构造函数,析构函数,多态等看起来不平凡的行为。而标准布局以为着可以通过memcpy进行拷贝。可以通过is_pod::value来判定一个类是否是POD。

    下面是平凡的详细定义:

    • 拥有平凡的默认构造函数和析构函数。(就是不定义构造函数和析构函数)
    • 拥有平凡的拷贝构造函数和移动构造函数。(不定义)
    • 拥有平凡的拷贝复制运算符和移动赋值运算符。(不定义)
    • 不能包含虚函数以及虚基类。

    标准布局的详细定义

    • 所有非静态成员都相同的访问权限。
    • 非静态成员不能同时出现在派生类和基类间。
    • 第一个非静态成员类型和基类不同。
    • 没有虚函数和虚基类。

    新手易学,老手易用

    1. 常用auto和decltype。

    auto不能推导的四种情况。

    
        void fun(auto x = 1){} //auto函数参数
        
        struct str{
            auto var = 10; //auto非静态成员变量
        }
        
        auto z[3] = x; //auto数组
        
        vector<auto> v = {1}; //auto模板参数
        
    
    1. 基于范围的for循环。
        vector<int> a = {1, 2, 3, 4};
        for(int i : a)
            cout << i << endl;
            
    

    提高类型安全

    强类型枚举

    enum class Type { General, Light, Medium, Heaby };
    

    它有如下优势:

    1. 强作用域,强类型枚举的名称不会被输出到其父作用域。
    2. 转换限制,值不可以和整形发生隐私转换。
    3. 可以指定底层类型,比如:
        
        enum class Type : char { General, Light, Medium, Heaby };
    

    堆内存管理:智能指针与垃圾回收

    智能指针

    必用

    垃圾回收

    c++只实现了最简单的垃圾回收。了解即可。

    垃圾回收一般可以分为两大类。

    1. 基于引用计数
      缺点是比较难处理“环形引用”。
    2. 基于跟踪
    • 标记-清除(标记:根据正在使用的对象查找引用的堆空间,并在堆空间上做标记。标记结束后,没有被标记的对象就是垃圾。清除:清除垃圾对象)
    • 标记-整理(整理:将活对象向左靠齐,还整理了碎片)
    • 标记-拷贝(拷贝:将堆分为FROM, TO,当某一块满了,将其中的活对象拷贝到另一块内存区域)

    提高性能及操作硬件的能力

    常量表达式

    常量表达式的作用,就是在于有时候我们知道某个变量是编译器常量,但是因为不是const或者在函数内,所以编译器就是不给过。下面有个例子:

    
        int main()
        {
            int n = 3;
            int arr[n];
            return 0;
        }
    

    在我们的教材中,要求定义arr必须使用编译器常量作为数组下标。所以,上面的写法应该是错的。但是!!!居然通过了编译,通过查阅资料,说这是c++的新特性。好吧,换个例子。

    
        int main()
        {
            int n = 3;
            enum { a = 3 }; //错误
            
            switch(cond){
                case getConst()://错误
                    break;
            }
            
            return 0;
        }
    

    看吧,上面报错了。

    常量表达式函数

    • 函数体只有单一的return返回语句。
    • 函数必须有返回值(不能是void函数)。
    • 在使用前必须有定义。
    • return语句不能使用非常量表达式函数,全局数据,且必须是一个常量表达式。

    常量表达式值

    const int i = 1;
    constexpr int j = 1;
    

    它们几乎没有区别,区别我就不说了。

    用常量表达式进行元编程

    因为常量表达式是在编译器展开,所以递归调用就会产生模板元编程类似的效果。

    
        constexpr int Fibonacci( int n ){
            return (n == 1) ? 1 : ((n == 2) ? 1 : Fibonacci(n - 1) + Fibonacci(n -2));
        }
    

    变长模板

    • 变长类模板常用方式
    
    
        //变长模板的声明
        template <typename... Elements> class tuple;
        
        //递归的偏特化定义
        template <typename Head, typename... Tail>
        class tuple<Head, Tail...> : private tuple<Tail...>{
            Head head;
        }
        
        //边界条件
        template<> class tuple<> {};
    
    • 变长函数模板常用方式
    
        #include <iostream>
        using namespace std;
        
        //结束条件
        void test()
        {
            return;
        }
    
        template<typename T, typename... Args>
        void test(T t, Args... args)
        {
            cout << t << endl;
            test(args...);
        }
    
        int main()
        {
            test(1,2,3,4,5,6,7,8);
        }
    

    原子类型

    • 理解c++原子操作内存模型。

    为了充分利用CPU的多级流水线模型,编译器处理器都可能将我们所写的代码顺序打乱。这可能对我们编写的进行原子操作的代码的正确性造成影响。因此,在使用原子类型的时候,必须对内存模型有所了解。

    一般而言,常用的内存模型有以下几种:

    • memory_order_seq_cst : 全部按照顺序执行(原子操作默认的内存模型)。
    • memory_order_acquire : 所有后续读操作都在本条操作后面完成。
    • memory_order_release : 所有之前的写操作必须在本条之前完成。

    通常而言,我们可以通过memory_order_acquire,memory_order_release写出无锁的高性能程序。

        atomic<int> a;
        atomic<int> b;
        int Thread1(int){
            int t = 1;
            a.store(t, memory_order_relaxed);
            b.store(2, memory_order_release); //本操作之前的所有写原子操作必须完成。
        }
        
        int Thread2(int){
            //本操作之后的读原子操作必须等该条语句执行。
            while(b.load(memory_order_acquire) != 2);
            cout << a.load(memory_order_relaxed) << endl;
        }
        
    

    线程局部存储

        int thread_local errCode;
    

    很简单,不解释。

    quick_exit和at_quick_exit

    因为exit()需要在函数结束的时候调用全局对象的析构函数,所以如果析构函数里面有加锁,或则delete之类的操作,在多线程的情况下可能对产生错误。

    quick_exit与exit的区别就是不执行全局对象的析构函数。
    at_quick_exit的作用是注册退出时执行的函数的。

    为改变思考方式而改变

    指针空值——nullptr

    一般情况下,NULL是个宏定义

    #define NULL 0
    

    可能会产生二义性。

    注意:nullptr不能像bool隐式转换。
    if(nullptr)和if(nullpty == 0)之类的用法都是不允许的。

    默认函数的控制

    =default;使用默认函数版本。
    =delete;删除编译器生成的默认函数。
    

    lambda函数

    一般格式如下:
    auto fun = [=](int a)->int{return a};

    • [] : 里面是捕捉列表,能够捕捉父作用域中的变量。(使用值传递,需要注意捕捉列表里面的值不会随外界改变)
    • () : 参数,可以省略。
    • ->return-type :返回值,能够推导出的情况可以省略。
    • {} : 函数体。

    lambda函数是仿函数的语法糖,所有lambda函数都能转换为一个仿函数。

    融入实际应用

    对齐支持

    c++11可以获取和指定数据的对齐方式。

    alignas(8) char c;//指定为按8为对齐
    int n = alignof(double)//获取按多少位数据对齐
    
    //可以将ptr指向的大小为space的内存的对齐方式进行调整,将ptr开始的size大小的数据调整为按alignment对齐。
    align(std::size_t alignment, std::size_t size, void *&ptr, std::size_t& space);
  • 相关阅读:
    IdentityServer4系列 | 资源密码凭证模式
    IdentityServer4系列 | 客户端凭证模式
    IdentityServer4系列 | 快速搭建简易项目
    Java9系列第九篇-对HTTP2协议的支持与非阻塞HTTP-API
    跨站资源共享CORS原理深度解析
    Java9系列第8篇-Module模块化编程
    Java9系列第7篇:Java.util.Optional优化与增强
    Kubernetes的Local Persistent Volumes使用小记
    CoProcessFunction实战三部曲之三:定时器和侧输出
    CoProcessFunction实战三部曲之二:状态处理
  • 原文地址:https://www.cnblogs.com/biterror/p/6909608.html
Copyright © 2011-2022 走看看