Я думаю, что вы, возможно, захотите перевернуть его: отказаться от владения созданными вами объектами (например, возвращая 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();
}