20 апреля 2006

Clever shared_ptr

Столкнулся с разработкой классов, подобных следующему:
class SomeClass
{
public:
SomeClass(T* Pointer, ...)
: _Pointer(Pointer) { }

private:
T* _Pointer;
}

Суть такова: передаем конструктору класса указатель на некоторый объект, он запоминает его и использует в своих целях.

Если мы динамически создали такой объект специально для передачи его в такой конструктор, то неплохо бы, чтобы деструктор этого класса автоматически удалил его из динамической памяти:
class SomeClass
{
public:
SomeClass(T* Pointer, ...)
: _Pointer(Pointer) { }

private:
auto_ptr<T> _Pointer;
}

SomeClass SomeInstance(new T(), ...);

Однако такой вариант предполагает, что мы обязательно будем создавать объекты типа T динамически, и экземпляр класса SomeClass будет владеть ими. Что не позволит:
1. передавать ссылки на объекты, которыми владеет кто-либо другой:
T Array[3];
SomeClass SomeInstance(&Array[2], ...);

2. использовать конструктор копирования и оператор присваивания, генерируемый компилятором по умолчанию (т.к. при копировании auto_ptr "донор" теряет ссылку).

Второй пункт можно обойти заменой auto_ptr на shared_ptr. Тем же самым методом можно обойти и первый пункт, если добавить немного кода:
struct NullDeleter
{
void operator()(const void *p) { /* ничего не делаем */ }
};

class SomeClass
{
public:
SomeClass(shared_ptr<T> Pointer, ...)
: _Pointer(Pointer) { }

private:
shared_ptr<T> _Pointer;
}

// передаем владение
SomeClass SomeInstance(new T(), ...);

// владение не передаем
T Array[3];
SomeClass SomeInstance(shared_ptr<T>(&Array[2], NullDeleter(), ...);

Плюс такого решения еще в том, что можно помимо двух крайностей "владеет"/"не владеет", наш класс получает третью возможность - "совладеет":
vector<shared_ptr<T> > v;
v.insert(new T());
...
SomeClass SomeInstance(v[0], ...);
...
v.clear();

Минус этого решение в том, что shared_ptr имеет много оверхеда (как по использованию памяти, так по процессорному времени) по сравнению с написанием специализированного класса, поддерживающего только два состояния: "владеет"/"не владеет".

Комментариев нет: