zoukankan      html  css  js  c++  java
  • 第23课 可变参数模板(4)_Optional和Lazy类的实现

    1. optional类的实现

    (1)optional的功能

      ①optional<T>的内部存储空间可能存储了T类型的值,也可能没有。只有当optional被T初始化之后,这个optional才是有效的。否则是无效的。它实现了未初始化的概念

      ②optional可以用于解决函数返回无效值的问题。当函数返回一个未初始化的Optional对象时,表明函数正确执行了,只是结果不是有用的值。

      ③举例:optional<int> op; //未被初始化。 optional<int> op = 1; //初始化。

    (2)实现细节

      ①由于optional<T>需要容纳T的值,所以需要一个缓冲区来保存它,但考虑到内存对齐,需要将T指定在对齐的位置上。可以通过std::alignment_of <T>::value来获取T的内存对齐大小。并通过std::aligned_storage<sizeof(T), aligned(T)>来定义T的内存对齐类型(该模板的本质就重新定义T经对齐后的一种新类型)。

    template<unsigned size, unsigned alignment>
    struct aligned_storage
    {
      using type = struct { alignas(alignment) unsigned char data[size]; };
    };

      ②std::aligned_storage一般和placement_new结合使用(见Optional类的create函数),用于初始化由std::aligned_storage定义的一片内存空间。

      ③增加一个m_hasInit标记来记录T空间是否己经初始化。

    【编程实验】Optional类的实现

    //Optional.hpp

    #ifndef _OPTIONAL_H_
    #define _OPTIONAL_H_
    
    #include <type_traits>
    #include <utility>  //for std::forward
    #include <stdexcept>
    
    template <typename T>
    class Optional
    {
        //std::alignment_of<T>::value获取T的内存对齐大小,std::aligned_storage将T重定义为对齐后的类型
        using data_t = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
    private:
        data_t m_data;  //内存对齐缓冲区
        bool m_hasInit; //是否己经初始化
    private:
        //调用placement_new来创建T对象
        template<class... Args>
        void create(Args... args) //可以接收左右值
        {
            new (&m_data) T(std::forward<Args>(args)...); //调用T的构造函数来初始化m_data空间
            m_hasInit = true;
        }
    
        //销毁缓冲区的对象
        void destroy()
        {
            if(m_hasInit){
                m_hasInit = false;
                ((T*)(&m_data))->~T(); //调用T的析构函数
            }
        }
    
        //缓冲区的拷贝
        void copy(const data_t& val)
        {
            destroy();
            new (&m_data) T(*((T*)(&val)));
        }
    
        //缓冲区的移动
        void move(data_t&& val)
        {
            destroy();
    
            //调用T的移动构造函数进行移动
            new (&m_data) T(std::move(*((T*)(&val))));
        }
    
        //Optional赋值操作(左值版本)
        void assign(const Optional& other)
        {
            if(other.isInit()){
                copy(other.m_data);
                m_hasInit = true;
            }else{
                destroy();
            }
        }
    
        //Optional赋值操作(右值版本)
        void assign(Optional&& other)
        {
            if(other.isInit()){
                move(std::move(other.m_data));
                m_hasInit = true;
    
                other.destroy(); //other失去资源控制权
            }else{
                destroy();
            }
        }
    
    public:
        Optional():m_hasInit(false){};
        Optional(const T& v)
        {
            create(v);
        }
    
        Optional(T&& v) : m_hasInit(false)
        {
            create(std::move(v));
        }
    
        Optional(const Optional& other) : m_hasInit(false)
        {
            if(other.isInit()){
                assign(other);
            }
        }
    
        Optional(Optional&& other) : m_hasInit(false)
        {
            if(other.isInit()){
                assign(std::move(other));
                other.destroy();
            }
        }
    
        //根据参数创建对象,如emplace(1,2);
        template<class ...Args>
        void emplace(Args&& ...args)
        {
            destroy();
            create(std::forward<Args>(args)...);
        }
    
        Optional& operator=(const Optional& other)
        {
            assign(other);
            return *this;
        }
    
        Optional& operator=(Optional&& other)
        {
            assign(std::move(other));
            return *this;
        }
    
        explicit operator bool() const //类型转换函数,如if(op)
        {
            return isInit();
        }
    
        T& operator*()
        {
            if(isInit()){
                return *((T*)(&m_data));
            }
    
            throw std::logic_error{"try to get data in a Optional which is not initialized"};
        }
    
        const T& operator*() const
        {
            if(isInit()){
                return *((T*)(&m_data));
            }
    
            throw std::logic_error{"try to get data in a Optional which is not initialized"};
        }
    
        T* operator->()
        {
            return &operator*();
        }
    
        const T* operator->() const
        {
            return &operator*();
        }
    
        bool operator==(const Optional<T>& rhs) const
        {
            bool bInit = bool(*this);
            return ( !bInit != (!rhs) ? //*this和rhs中一个初始化,一个未初始化
                     false :
                     !bInit ? true : (*(*this) == (*rhs)) //两者都未初始化,返回true
                                                          //两者都初始化时,比较两个T对象是否相等
                   );
        }
    
        bool operator<(const Optional<T>& rhs) const
        {
            bool bInit = bool(*this);
            return !rhs ? false : (!bInit ? true : (*(*this) < (*rhs)));
        }
    
        bool operator!=(const Optional<T>& rhs) const
        {
            return !(*this == rhs);
        }
    
        bool isInit() const {return m_hasInit;}
    
        ~Optional()
        {
            destroy();
        }
    
    };
    
    #endif

    //main.cpp

    #include "Optional.hpp"
    #include <iostream>
    
    using namespace std;
    
    struct Test
    {
        Test() : m_a(0), m_b(0){}
        Test(int a, int b) : m_a(a), m_b(b){}
    
        int m_a;
        int m_b;
        void show()
        {
            cout << "a = "<< m_a << ", b = " << m_b << endl;
        }
    };
    
    void TestOptional()
    {
        const Optional<string> a("ok");
        Optional<string> b("ok");
        Optional<string> c("aa");
        Optional<string> d = b;
        Optional<string> e;
    
        cout << (e<b) << endl;  //true
        cout << (b==d) << endl; //true
        cout << *c << endl;
        //cout << *e << endl; //error
    
        Optional<Test> op;
        op.emplace(1, 2);
        (*op).show();
    
        Test t;
        if(op)     //判断op是否被初始化
           t = *op;
        t.show();
    
        op.emplace(3, 4);
        t = *op;
        t.show();
    }
    
    int main()
    {
        TestOptional();
        return 0;
    }
    
    /*输出结果:
    e:StudyC++1123>g++ -std=c++11 test.cpp
    e:StudyC++1123>a.exe
    1
    1
    aa
    a = 1, b = 2
    a = 1, b = 2
    a = 3, b = 4
    */

    2. 惰性求值:Lazy类的实现

    (1)Lazy类的功能

      ①惰性求值一般用于函数式编程语言中。

      ②可实现函数的延迟调用,函数参数被绑定后并不立即调用,而是在以后的某个时候调用。

      ③可实现大对象数据的延迟加载。如当初始化某个对象时,该对象引用了一个大对象,但很多时候并不马上获取该对象的数据,就可以延迟加载这个大对象。

    (2)实现细节

      ①借助lambda表达式,将函数封装到lambda表达式中,而不是马上求值,在需要的时候再调用lambda表达式去求值

      ②std::function用于保存传入的函数,并延迟到后面需要使用值的时候才执行,函数的返回值放到一个Optional对象中。Optional对象是否被初始化,来判断大对象是否己加载。

      ③辅助函数lazy的作用是方便使用Lazy类, Lazy<T>中的T用来表示返回值类型大对象的类型这也是被封装的函数返回值类型,可利用std::result_of来获取该返回值类型。

    【编程实验】Lazy类的实现

    //Lazy.hpp

    #ifndef _LAZY_H_
    #define _LAZY_H_
    
    #include "Optional.hpp"
    #include <functional>
    #include <type_traits>
    #include <utility>  //for std::forward
    
    template<typename T>
    struct Lazy
    {
    private:
        Optional<T> m_value;
        std::function<T()> m_func;
    public:
        Lazy(){}
    
        //保存需要延迟执行的函数及其参数
        template<typename Func, typename ...Args>
        Lazy(Func&& f, Args&&... args)
        {
            m_func = [&f, &args...]{return f(args...);};
        }
    
        //延迟执行,将结果放到Optional中缓存起来,下次不用重新计算就可以直接返回结果
        T& value()
        {
            if(! m_value.isInit()){
                m_value = m_func();
            }
    
            return *m_value;
        }
    
        bool isValueCreated() const
        {
            return m_value.isInit();
        }
    };
    
    //辅助函数,简化Lazy的调用
    template<class Func, typename... Args>
    Lazy<typename std::result_of<Func(Args...)>::type>   //返回值类型Lazy<T>
    lazy(Func&& fun, Args&&... args)
    {
        using ret_type_t = typename std::result_of<Func(Args...)>::type;
        return Lazy<ret_type_t>(std::forward<Func>(fun), std::forward<Args>(args)...);
    }
    
    
    #endif // _LAZY_H_

    //main.cpp

    #include "Lazy.hpp"
    #include <iostream>
    #include <memory>  //for std::shared_ptr
    
    using namespace std;
    
    struct BigObject
    {
        BigObject()
        {
            cout << "lazy load big object..." << endl;
        }
    };
    
    struct Test
    {
    private:
        Lazy<std::shared_ptr<BigObject>> m_obj;
    public:
        Test()
        {
            m_obj = lazy([]{return std::make_shared<BigObject>();});
        }
    
        void load()
        {
            m_obj.value();
        }
    };
    
    int Foo(int x)
    {
        return x * 2;
    }
    
    void TestLazy()
    {
        //带参数的普通函数
        int a = 4;
        auto lazy1 = lazy(Foo, a);
        cout << lazy1.value() << endl;  //8
    
        //不带参数的lambda表达式
        Lazy<int> lazy2 = lazy([]{return 12;});
        cout << lazy2.value() << endl;   //12
    
        //带参的function
        std::function<int(int)> f = [](int x){return x + 3;};
        auto lazy3 = lazy(f, a);
        cout << lazy3.value() << endl;  //7
    
        //延迟加载大对象
        Test t;
        t.load();  //lazy load big object...
    }
    
    int main()
    {
        TestLazy();
        return 0;
    }
  • 相关阅读:
    洛谷 P2867 [USACO06NOV]大广场Big Square
    考前冲刺 1
    洛谷 P1939 【模板】矩阵加速(数列)
    洛谷 P3390 【模板】矩阵快速幂
    洛谷 P3376 【模板】网络最大流
    cogs P1578【模板】 次小生成树初级练习题
    洛谷 P3379 【模板】最近公共祖先(LCA)
    10-1 集合之Map
    【Web Components】
    【Animations】
  • 原文地址:https://www.cnblogs.com/5iedu/p/7820399.html
Copyright © 2011-2022 走看看