如何避免C ++中的连续释放/分配? [英] How to avoid successive deallocations/allocations in C++?

查看:36
本文介绍了如何避免C ++中的连续释放/分配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码:

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

    void calledVeryOften(…)
    {
        if (b)
            delete b;

        b = new B(param1, param2, param3, param4);
    }
};

我的目标:我需要最大化性能,在这种情况下,这意味着最小化内存分配量.

My goal: I need to maximize performance, which, in this case, means minimizing the amount of memory allocations.

此处显而易见的事情是将B* b;更改为B b;.我看到这种方法有两个问题:

The obvious thing to do here is to change B* b; to B b;. I see two problems with this approach:

  • 我需要在构造函数中初始化b.由于我不知道b是什么,这意味着我需要将伪值传递给B的构造函数. IMO,这很丑.
  • calledVeryOften()中,我必须做这样的事情:b = B(…),这是错误的,原因有两个:
    • b的析构函数不会被调用.
    • 将构造B的一个临时实例,然后将其复制到b中,然后将调用该临时实例的析构函数.可以避免复制和析构函数调用.更糟糕的是,调用析构函数很可能导致不良行为.
    • I need to initialize b in the constructor. Since I don't know what b will be, this means I need to pass dummy values to B's constructor. Which, IMO, is ugly.
    • In calledVeryOften(), I'll have to do something like this: b = B(…), which is wrong for two reasons:
      • The destructor of b won't be called.
      • A temporary instance of B will be constructed, then copied into b, then the destructor of the temporary instance will be called. The copy and the destructor call could be avoided. Worse, calling the destructor could very well result in undesired behavior.

      那么我必须避免使用什么解决方案?请记住:

      So what solutions do I have to avoid using new? Please keep in mind that:

      • 我只能控制A.我不能控制B,也不能控制A的用户.
      • 我想保持代码尽可能的整洁和可读.

      推荐答案

      我喜欢Klaim的答案,所以我写得很快.我并没有要求完全正确,但是对我来说,它看起来不错. (即唯一的测试是下面的示例main)

      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)

      这是一个通用的惰性初始化器.该对象的空间分配一次,并且该对象从null开始.然后,您可以create,覆盖先前的对象,而无需分配新的内存.

      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.

      它实现所有必需的构造函数,析构函数,复制/分配,交换,yadda-yadda.在这里,您去了:

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

      #include <cassert>
      #include <new>
      
      template <typename T>
      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 <typename A1>
          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 <typename T>
      void swap(lazy_object<T>& pLhs, lazy_object<T>& 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 <typename T>
          void swap(lazy_object<T>& pLhs, lazy_object<T>& pRhs)
          {
              pLhs.swap(pRhs);
          }
      }
      
      // test use
      #include <iostream>
      
      int main(void)
      {
          // basic usage
          lazy_object<int> i;
          i.create();
          i.get() = 5;
      
          std::cout << i.get() << std::endl;
      
          // asserts (not created yet)
          lazy_object<double> d;
          std::cout << d.get() << std::endl;
      }
      

      在您的情况下,只需在您的班级中创建一个成员:lazy_object<B>,您就完成了.无需手动发布,也无需制作复制构造函数,析构函数等.一切都在您的小型可重用的类中处理. :)

      In your case, just create a member in your class: lazy_object<B> 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. :)

      消除了对向量的需要,应该节省一些空间和其他内容.

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

      这使用aligned_storagealignment_of来使用堆栈而不是堆.我使用了 boost ,但是此功能同时存在于TR1和C ++ 0x中.我们失去了复制的能力,因此无法进行交换.

      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 <boost/type_traits/aligned_storage.hpp>
      #include <cassert>
      #include <new>
      
      template <typename T>
      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 <typename A1>
          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<sizeof(T),
                      boost::alignment_of<T>::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 <iostream>
      
      int main(void)
      {
          // basic usage
          lazy_object_stack<int> i;
          i.create();
          i.get() = 5;
      
          std::cout << i.get() << std::endl;
      
          // asserts (not created yet)
          lazy_object_stack<double> d;
          std::cout << d.get() << std::endl;
      }
      

      然后我们去.

      这篇关于如何避免C ++中的连续释放/分配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆