How to avoid successive deallocations/allocations in C++?

前端 未结 10 1046
失恋的感觉
失恋的感觉 2021-02-07 18:26

Consider the following code:

class A
{
    B* b; // an A object owns a B object

    A() : b(NULL) { } // we don\'t know what b will be when constructing A

             


        
10条回答
  •  Happy的楠姐
    2021-02-07 19:18

    I liked Klaim's answer, so I wrote this up real fast. I don't claim perfect correctness but it looks pretty good to me. (i.e., the only testing it has is the sample main below)

    It's a generic lazy-initializer. The space for the object is allocated once, and the object starts at null. You can then create, over-writing previous objects, with no new memory allocations.

    It implements all the necessary constructors, destructor, copy/assignment, swap, yadda-yadda. Here you go:

    #include 
    #include 
    
    template 
    class lazy_object
    {
    public:
        // types
        typedef T value_type;
        typedef const T const_value_type;
        typedef value_type& reference;
        typedef const_value_type& const_reference;
        typedef value_type* pointer;
        typedef const_value_type* const_pointer;
    
        // creation
        lazy_object(void) :
        mObject(0),
        mBuffer(::operator new(sizeof(T)))
        {
        }
    
        lazy_object(const lazy_object& pRhs) :
        mObject(0),
        mBuffer(::operator new(sizeof(T)))
        {
            if (pRhs.exists())
            {
                mObject = new (buffer()) T(pRhs.get());
            }
        }
    
        lazy_object& operator=(lazy_object pRhs)
        {
            pRhs.swap(*this);
    
            return *this;
        }
    
        ~lazy_object(void)
        {
            destroy();
            ::operator delete(mBuffer);
        }
    
        // need to make multiple versions of this.
        // variadic templates/Boost.PreProccesor
        // would help immensely. For now, I give
        // two, but it's easy to make more.
        void create(void)
        {
            destroy();
            mObject = new (buffer()) T();
        }
    
        template 
        void create(const A1 pA1)
        {
            destroy();
            mObject = new (buffer()) T(pA1);
        }
    
        void destroy(void)
        {
            if (exists())
            {
                mObject->~T();
                mObject = 0;
            }
        }
    
        void swap(lazy_object& pRhs)
        {
            std::swap(mObject, pRhs.mObject);
            std::swap(mBuffer, pRhs.mBuffer);
        }
    
        // access
        reference get(void)
        {
            return *get_ptr();
        }
    
        const_reference get(void) const
        {
            return *get_ptr();
        }
    
        pointer get_ptr(void)
        {
            assert(exists());
            return mObject;
        }
    
        const_pointer get_ptr(void) const
        {
            assert(exists());
            return mObject;
        }
    
        void* buffer(void)
        {
            return mBuffer;
        }
    
        // query
        const bool exists(void) const
        {
            return mObject != 0;
        }
    
    private:
        // members
        pointer mObject;
        void* mBuffer;
    };
    
    // explicit swaps for generality
    template 
    void swap(lazy_object& pLhs, lazy_object& pRhs)
    {
        pLhs.swap(pRhs);
    }
    
    // if the above code is in a namespace, don't put this in it!
    // specializations in global namespace std are allowed.
    namespace std
    {
        template 
        void swap(lazy_object& pLhs, lazy_object& pRhs)
        {
            pLhs.swap(pRhs);
        }
    }
    
    // test use
    #include 
    
    int main(void)
    {
        // basic usage
        lazy_object i;
        i.create();
        i.get() = 5;
    
        std::cout << i.get() << std::endl;
    
        // asserts (not created yet)
        lazy_object d;
        std::cout << d.get() << std::endl;
    }
    

    In your case, just create a member in your class: lazy_object and you're done. No manual releases or making copy-constructors, destructors, etc. Everything is taken care of in your nice, small re-usable class. :)

    EDIT

    Removed the need for vector, should save a bit of space and what-not.

    EDIT2

    This uses aligned_storage and alignment_of to use the stack instead of heap. I used boost, but this functionality exists in both TR1 and C++0x. We lose the ability to copy, and therefore swap.

    #include 
    #include 
    #include 
    
    template 
    class lazy_object_stack
    {
    public:
        // types
        typedef T value_type;
        typedef const T const_value_type;
        typedef value_type& reference;
        typedef const_value_type& const_reference;
        typedef value_type* pointer;
        typedef const_value_type* const_pointer;
    
        // creation
        lazy_object_stack(void) :
        mObject(0)
        {
        }
    
        ~lazy_object_stack(void)
        {
            destroy();
        }
    
        // need to make multiple versions of this.
        // variadic templates/Boost.PreProccesor
        // would help immensely. For now, I give
        // two, but it's easy to make more.
        void create(void)
        {
            destroy();
            mObject = new (buffer()) T();
        }
    
        template 
        void create(const A1 pA1)
        {
            destroy();
            mObject = new (buffer()) T(pA1);
        }
    
        void destroy(void)
        {
            if (exists())
            {
                mObject->~T();
                mObject = 0;
            }
        }
    
        // access
        reference get(void)
        {
            return *get_ptr();
        }
    
        const_reference get(void) const
        {
            return *get_ptr();
        }
    
        pointer get_ptr(void)
        {
            assert(exists());
            return mObject;
        }
    
        const_pointer get_ptr(void) const
        {
            assert(exists());
            return mObject;
        }
    
        void* buffer(void)
        {
            return mBuffer.address();
        }
    
        // query
        const bool exists(void) const
        {
            return mObject != 0;
        }
    
    private:
        // types
        typedef boost::aligned_storage::value> storage_type;
    
        // members
        pointer mObject;
        storage_type mBuffer;
    
        // non-copyable
        lazy_object_stack(const lazy_object_stack& pRhs);
        lazy_object_stack& operator=(lazy_object_stack pRhs);
    };
    
    // test use
    #include 
    
    int main(void)
    {
        // basic usage
        lazy_object_stack i;
        i.create();
        i.get() = 5;
    
        std::cout << i.get() << std::endl;
    
        // asserts (not created yet)
        lazy_object_stack d;
        std::cout << d.get() << std::endl;
    }
    

    And there we go.

提交回复
热议问题