| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Общие вопросы > Проектирование итератора |
| Автор: Lazin 17.12.2007, 14:23 | ||
Возникла следующая задача, есть сложная структура данных, элементы которой можно читать только последовательно, для этого случая идеально подходит паттерн "итератор". Вопрос в том, где его лучше описывать - внутри класса для доступа к которому он используется, или лучше его описать отдельно. Я понимаю, что оба решения примерно одинаковы, вопрос в том как будет более аккуратно, с учетом возможных последующих изменений.
|
| Автор: UnrealMan 17.12.2007, 15:01 |
| Ну, лично мне бы не понравилось видеть определение одного большого класса (где много членов) внутри определения другого большого класса. А вот поместить внутрь определения класса только объявление вложенного класса - это можно. В случае ошибок в диагностическом сообщении компилятора будет хорошо видно, к чему относится вложенный класс. |
| Автор: UnrealMan 17.12.2007, 16:06 | ||
Наверное, шаблонными аргументами тут должно быть что-то другое. Некоторые компиляторы в сообщении об ошибке могут сослаться на CBlockIterator<....>, а не CBlockPacker<....>::Iterator. Вот я о чём.
Что-то я не понял, где он перестанет работать. Приведи пример. |
| Автор: baldina 17.12.2007, 18:22 |
| описывать снаружи имеет смысл только в случае, если у тебя несколько классов с одинаковым интерфейсом доступа, для которых ты можешь использовать похожую реализацию итераторов. насчет дружбы идея понятна, понятна и проблема. потому лучше их и не дружить. |
| Автор: Sartorius 17.12.2007, 18:29 |
| ИМХО лучше помещать итератор в класс, с которым он будет работь. К чему плодить шаблоны? |
| Автор: UnrealMan 17.12.2007, 18:35 |
А вот мне проблема не понятна. А как тогда обращаться к непубличным членам? Добавлено через 6 минут и 12 секунд У STL разные реализации. Добавлено через 11 минут и 54 секунды Например, в реализации STL от GNU, которая имеется у меня, итератор списка является невложенным классом. |
| Автор: JackYF 17.12.2007, 19:46 | ||
Подтверждаю: и у меня тоже. Так что... не факт, не факт. Я бы сказал так: если хватает публичных методов/полей класса для построения итератора - делаем внешний, если не хватает, то тут... не знаю, я склоняюсь ко внешнему, но, имхо, и так, и так сойдёт. |
| Автор: Lazin 18.12.2007, 09:31 | ||||||
вопросов больше нет
это я малость ступил, я подумал что отношение дружбы не наследуется, поэтому тот-же шаблон не будет работать для производного класса(так как другом он объявлен в базовом), но никто не мешает мне объявить итератор другом и в производном. ps есть еще один вариант реализации итератора, это когда ф-ии для перебора элементов коллекции, находятся в классе коллекции. |
| Автор: Earnest 18.12.2007, 10:24 | ||
Не вижу связи. Вложенный класс точно так же не может обращаться к непубличным методам, если он не друг. И с шаблонами все то же, только параметры немного в другом месте описаны. Так что речь только об именах. Вложенный класс - меньше имен. Но. Если в дальнейшем возникнет необходимость в неполных объявлениях, на то вложенный клас без включения хедера не сошлешься, тогда как на внешний - элементарно. Резюме примерно такое: с точки зрения функуциональности - по барабану. Здесь нужно учитывать скорее публичность данного класса. Если это дело нужно сугубо локально, то вложенный класс - хорошее решение. Если речь идет о более широком использовании, лучше внешний клас. А чтобы крепче связать его с целевым контейнером и не громоздить многоэтажные имена, можно все засунуть в пространство имен. Важнее сделать нормальный интерфейс: раз уж называешь iterator, и то вести себя он должен как кошерный STL-итератор. Здесь может помочь boost::iterator_adapter - очень хорошая вешь, три четверти работы за тебя сделает. |
| Автор: baldina 18.12.2007, 16:10 | ||
дружба - очень сильная связь. надо иметь достаточно оснований для связи посредством дружбы. именно по этой причине дружба в С++ сделана нетранзитивной. в данном случае дружба дает только доступ к внутреннему устройству класса, что не менее эффективно достигается вложенным классом. а если итератор более общий, то класс, для которого он предназначен, будет иметь некий интерфейс для использования итераторами (или чем-то инициализировать итератор). здесь дружба и не понадобится. а в силу того что дружба не транзитивна, красиво и правильно использовать класс итератора с другими классами не выйдет. |
| Автор: UnrealMan 18.12.2007, 22:39 | ||||
Earnest уже сказала, что по стандарту вложенный класс, в сравнении со всеми остальными классами, по умолчанию не имеет никаких привилегий касательно доступа к членам содержащего его класса. Хотя многие компиляторы это положение стандарта игнорируют и позволяют свободно обращаться к членам класса из его вложенного класса.
Что ещё за транзитивность дружбы такая? |
| Автор: baldina 18.12.2007, 22:59 | ||
не транзитивность. это означает, что если A дружит с B, а B дружит с C, то это не означает, что А дружит с С |
| Автор: baldina 18.12.2007, 23:39 | ||||
Согласен, я был неправ. |
| Автор: chipset 19.12.2007, 08:44 | ||||||
ИМХО, тут больше подходит "stack." Что ты будешь делать я вызову *(iter+=2)?
Адназначна |
| Автор: Lazin 19.12.2007, 09:18 | ||||
а я не реализую операцию +=
Представь что итератор пользуется несколькими ф-ями из контейнера, для перехода по элементам. Эти ф-ии логично сделать закрытыми, так как клиентам класса не нужно их использовать напрямую, а для этого приходится делать класс другом. Дружба действительно оч. сильная связь, следующая после наследования по силе, но итератор по определению должен быть связан с классом коллекции, так как это всего-лишь вспомогательный класс для доступа к данным коллекции. |
| Автор: chipset 19.12.2007, 11:08 | ||||||
Дружба сильнее наследования поскольку она снимает private а наследование (и то, только public) снимает лишь protected. А вообще чем больше смотрю на твою проблему, тем более она мне напоминает паттерн proxy. А ещё точнее secure proxy. Потому-что все-таки итератор ассоциируется с random access. Если ты запретишь += то я, как юзер, ваще не вникну в чем дело. Да! Мне кажется что основной класс похож на storage и ничего не делает кроме того что хранит данные. ИМХО имеет смысл тупо написать:
А уже потом декорировать как хочешь итераторами и всякой фигней. ЗЫ. Пока модератора нет, вот ещё кусок кода на обозрение публики.
|
| Автор: Lazin 19.12.2007, 13:01 | ||||||
Даже не хранит, а является посредником, но это не важно. Важно то что элементы storage можно получать только строго по порядку, так как туда записываются только изменения(как в cvs репозитории Методы для вычисления очередного элементы содержит класс - storage? так как только он знает формат их хранения, а итератор их вызывает, и хранит промежуточные данные. Так что сдесь скорее такая схема
и использовать можно будет так
жаль в языке нельзя ограничить отношение дружбы несколькими нужными ф-ями |
| Автор: UnrealMan 19.12.2007, 22:08 |
Когда это понятия iterator и random access iterator стали означать одно и то же? |
| Автор: Earnest 25.12.2007, 19:53 | ||
Дык выдели этот функционал в отдельный класс и именно с ним дружи, если уж за чистоту борешься... |