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);
  • 相关阅读:
    查看端口有没有被占用
    微信公众号2()
    How to insert a segment of noise to music file
    puppet practice
    Docker Commands
    LempelZiv algorithm realization
    The algorithm of entropy realization
    Java network programmingguessing game
    Deploy Openstack with RDO and Change VNC console to Spice
    puppet overview
  • 原文地址:https://www.cnblogs.com/biterror/p/6909608.html
Copyright © 2011-2022 走看看