Рассмотрим следующую структуру класса:
class Filter
{
virtual void filter() = 0;
virtual ~Filter() { }
};
class FilterChain : public Filter
{
FilterChain(collection<Filter*> filters)
{
// copies "filters" to some internal list
// (the pointers are copied, not the filters themselves)
}
~FilterChain()
{
// What do I do here?
}
void filter()
{
// execute filters in sequence
}
};
Я выставляю класс в библиотеке, поэтому у меня нет контроля над тем, как он будет использоваться.
В настоящее время у меня есть некоторые проблемы с дизайном, связанные с владением объектами Filter
, на которые FilterChain
ссылается. Более конкретно, вот два возможных сценария использования для FilterChain
:
- Сценарий A: некоторые функции в моей библиотеке создают (возможно, сложную) цепочку фильтров, выделяют память по мере необходимости и возвращают вновь выделенный объект
FilterChain
. Например, одна из этих функций создает цепочку фильтров из файла, которая может описывать произвольно сложные фильтры (включая цепочки фильтров цепочек фильтров и т. Д.). Пользователь функции отвечает за уничтожение объекта после выполнения задания.
- Сценарий B: пользователь имеет доступ к группе
Filter
объектов и хочет объединить их в цепочки фильтров особым образом. Пользователь создает FilterChain
объекты для собственного использования, а затем уничтожает их, когда завершает работу с ними. Filter
объекты не должны быть уничтожены при уничтожении FilterChain
, ссылающегося на них.
Теперь два простых способа управления владением объектом FilterChain
:
FilterChain
владеют Filter
объектами. Это означает, что объекты, на которые ссылается FilterChain
, уничтожаются в деструкторе FilterChain
. Что несовместимо со сценарием B.
FilterChain
не не владеет Filter
объектами. Это означает, что деструктор FilterChain
ничего не делает. Теперь существует проблема со сценарием A, потому что пользователь должен знать внутреннюю структуру всех Filter
объектов, задействованных для уничтожения их всех, не пропуская один, так как родительский FilterChain
не делает это сам. Это просто плохой дизайн, и просить утечки памяти.
Следовательно, мне нужно что-то более сложное. Мое первое предположение состоит в том, чтобы создать умный указатель с настраиваемым логическим флагом, указывающим, является ли умный указатель владельцем объекта. Тогда вместо того, чтобы брать набор указателей на Filter
объекты, FilterChain
будет брать набор умных указателей на Filter
объектов. Когда вызывается деструктор FilterChain
, он уничтожает умные указатели. Деструктор самого умного указателя затем уничтожит объект, на который указывают (Filter
объект) тогда и только тогда, когда установлен логический флаг, указывающий на владение.
Мне кажется, что эта проблема является обычной в C ++, но мои поиски в Интернете популярных решений или хитроумных шаблонов проектирования были не очень успешными. Действительно, auto_ptr
здесь не очень помогает, а shared_ptr
кажется излишним. Итак, мое решение - хорошая идея или нет?