Рассмотрим следующую структуру класса:
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
. Что несовместимо со сценарием Б.FilterChain
не владеетFilter
объектами. Это означает, что деструкторFilterChain
ничего не делает. Теперь есть проблема со сценарием A, потому что пользователь должен знать внутреннюю структуру всех задействованных объектовFilter
, чтобы уничтожить их все, не пропустив ни одного, поскольку родительFilterChain
не делает этого сам. Это просто плохой дизайн и запрос на утечку памяти.
Следовательно, мне нужно что-то более сложное. Мое первое предположение состоит в том, чтобы разработать интеллектуальный указатель с устанавливаемым логическим флагом, указывающим, владеет ли интеллектуальный указатель объектом. Тогда вместо набора указателей на Filter
объектов FilterChain
будет принимать набор интеллектуальных указателей на Filter
объектов. Когда вызывается деструктор FilterChain
, он уничтожает умные указатели. Затем деструктор самого интеллектуального указателя уничтожит объект, на который указывает указатель (объект Filter
) если и только если установлен логический флаг, указывающий на принадлежность.
У меня такое ощущение, что эта проблема распространена в C++, но мои поиски в Интернете популярных решений или умных шаблонов проектирования не увенчались успехом. На самом деле, auto_ptr
здесь не очень помогает, а shared_ptr
кажется излишним. Итак, мое решение является хорошей идеей или нет?