Слабые ресурсы и дизайн фабрики - PullRequest
2 голосов
/ 19 января 2012

Думая о том, как лучше всего подойти к следующему дизайну (псевдо-код C ++):

class Display {

  public:

   Bitmap GetBitmap(uint16 width, uint16 height);

  private:

   // A list of active bitmaps
   std::set<Bitmap> bitmaps;
}

... где Display - фабрика (и единственный владелец) для растровых объектов. Дисплей также должен отслеживать все выделенные растровые изображения в контейнере std::set и может в любое время отключить / удалить их (как в полноэкранном режиме).

Таким образом, битмап по сути является слабым дескриптором ресурса, и тот, кто его держит, должен будет проверить, действителен ли он, прежде чем его использовать.

Мое теоретическое решение сейчас заключается в использовании интеллектуальных указателей C ++:

  • GetBitmap() вернет weak_ptr<Bitmap> и, следовательно, может быть проверен на действительность с помощью weakBitmap.lock().
  • Внутренний растровый контейнер std :: set будет содержать shared_ptr<Bitmap>, чтобы можно было выдавать слабые растровые указатели.

Как лучше всего реализовать данный дизайн? Существует ли де-факто шаблон проектирования для данной функции, который мне не хватает?

1 Ответ

2 голосов
/ 19 января 2012

Я думаю, что вы, возможно, захотите перевернуть его: отказаться от владения созданными вами объектами (например, возвращая boost::shared_ptr<Bitmap>) и сохранить только слабые указатели на объекты в вашей внутренней коллекции (например, std::list<boost::weak_ptr<Bitmap>>).Всякий раз, когда пользователь запрашивает растровое изображение, блокируйте свою коллекцию и добавляйте туда новое растровое изображение.Если ваша коллекция представляет собой список, вы можете безопасно сохранить итератор для только что добавленного элемента и позже удалить его из коллекции, тем самым обрабатывая уничтожение растрового изображения.

Поскольку boost::shared_ptr::lock является атомарным, он также будет работать при многопоточности.Но вам все равно нужно охранять доступ к коллекции с помощью замка.

Вот пример кода:

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <boost/thread.hpp>

class Display;

class Bitmap : boost::noncopyable
{
  friend class Display;
  Bitmap(int, Display* d) : display(d)
  {}

  Display* display;
public:

  ~Bitmap(); // see below for definition
};

typedef boost::shared_ptr<Bitmap> BitmapPtr;

class Display
{
  typedef std::list<boost::weak_ptr<Bitmap>> bitmaps_t;
  typedef std::map<Bitmap*, bitmaps_t::iterator> bitmap_map_t;
  bitmaps_t     bitmaps_;
  bitmap_map_t  bitmap_map_;
  boost::mutex  mutex_;

  friend class Bitmap;
  void Remove(Bitmap* p)
  {
    boost::lock_guard<boost::mutex> g(mutex_);
    bitmap_map_t::iterator i = bitmap_map_.find(p);
    if (i != bitmap_map_.end())
    {
      bitmaps_.erase(i->second);
      bitmap_map_.erase(i);
    }
  }

public:
  ~Display()
  {
    boost::lock_guard<boost::mutex> g(mutex_);
    for (bitmaps_t::iterator i = bitmaps_.begin(); i != bitmaps_.end(); ++i)
    {
      BitmapPtr ptr = i->lock();
      if (ptr)
        ptr->display = NULL;
    }
  }

  BitmapPtr GetBitmap(int i)
  {
    BitmapPtr r(new Bitmap(i, this));
    boost::lock_guard<boost::mutex> g(mutex_);
    bitmaps_.push_back(boost::weak_ptr<Bitmap>(r));
    bitmap_map_[r.get()] = --bitmaps_.end();
    return r;
  }
};

Bitmap::~Bitmap()
{
  if (display)
    display->Remove(this);
}

Обратите внимание, что существует двусторонняя связь между Bitmap и Display - они оба содержат слабый указатель друг на друга, что означает, что они могутсвободно общаться.Например, если вы хотите, чтобы Display мог «уничтожать» растровые изображения, он может просто отключить их, обновив «display» до NULL, как показано в деструкторе Display.Display должен блокировать weak_ptr<Bitmap> каждый раз, когда он хочет получить доступ к растровому изображению (пример в деструкторе), но это не должно быть большой проблемой, если такое общение происходит не часто.

Для обеспечения безопасности потока вам может понадобиться блокировать Bitmap каждый раз, когда вы хотите «отключить» его, т. Е. Сбрасывать элемент отображения в NULL, и блокировать его каждый раз, когда вы хотите получить доступ к «display» из Bitmap.Поскольку это влияет на производительность, вы можете заменить Display* на weak_ptr<Display> в растровом изображении.Это также устраняет необходимость в деструкторе в Display.Вот обновленный код:

class Bitmap : boost::noncopyable
{
  friend class Display;
  Bitmap(int, const boost::shared_ptr<Display>& d) : display(d)
  {}

  boost::weak_ptr<Display> display;

public:
  ~Bitmap(); // see below for definition
};

typedef boost::shared_ptr<Bitmap> BitmapPtr;

class Display : public boost::enable_shared_from_this<Display>
              , boost::noncopyable
{
  //... no change here

public:
  BitmapPtr GetBitmap(int i)
  {
    BitmapPtr r(new Bitmap(i, shared_from_this()));
    boost::lock_guard<boost::mutex> g(mutex_);
    bitmaps_.push_back(boost::weak_ptr<Bitmap>(r));
    bitmap_map_[r.get()] = --bitmaps_.end();
    return r;
  }
};

Bitmap::~Bitmap()
{
  boost::shared_ptr<Display> d(display);
  if (d)
    d->Remove(this);
}

В этом случае «отключение» экранного указателя в Bitmap является поточно-ориентированным без явной синхронизации и выглядит так:

Display::DisableBitmap(bitmaps_t::iterator i)
{
  BitmapPtr ptr = i->lock();
  if (ptr)
    ptr->display.reset();
}
...