Может кто-нибудь указать мне на хорошие ресурсы для понимания и использования вложенных классов? У меня есть некоторые материалы, такие как Принципы программирования и такие вещи, как этот Центр знаний IBM - Вложенные классы
Но мне все еще трудно понять их цель. Может ли кто-нибудь помочь мне?
c++
nested
inner-classes
носящий очки
источник
источник
typedef
. 3. потому что они добавляют дополнительный уровень отступов в среде, где избежать длинных строк уже сложно 4. потому что вы объявляете два концептуально отдельных объекта в однойclass
декларации и т. Д.Ответы:
Вложенные классы хороши для сокрытия деталей реализации.
Список:
Здесь я не хочу показывать Node, так как другие люди могут решить использовать класс, и это помешало бы мне обновить мой класс, поскольку все, что выставлено, является частью общедоступного API и должно поддерживаться вечно . Делая класс закрытым, я не только скрываю реализацию, но и говорю, что она моя, и я могу изменить ее в любое время, чтобы вы не могли ее использовать.
Посмотрите
std::list
илиstd::map
они все содержат скрытые классы (или они?). Дело в том, что они могут или не могут, но поскольку реализация является частной и скрытой, создатели STL смогли обновить код, не влияя на то, как вы использовали код, или оставив много старого багажа, лежащего вокруг STL, потому что им нужно поддерживать обратную совместимость с некоторыми дураками, которые решили использовать класс Node, который был спрятан внутриlist
.источник
Node
не должны отображаться в заголовочном файле вообще.detail
соглашения: вместо этого, в зависимости от таких соглашений, нужно помнить о себе, лучше полагаться на компилятор, который отслеживает их для вас.Вложенные классы похожи на обычные классы, но:
Некоторые примеры:
Публично вложенный класс, чтобы поместить его в область видимости соответствующего класса
Предположим, вы хотите иметь класс,
SomeSpecificCollection
который будет агрегировать объекты классаElement
. Вы можете затем либо:объявлять два класса:
SomeSpecificCollection
иElement
- плохо, потому что имя «Элемент» достаточно общее, чтобы вызвать возможное столкновение именввести пространство имен
someSpecificCollection
и объявить классыsomeSpecificCollection::Collection
иsomeSpecificCollection::Element
. Нет риска столкновения имен, но может ли оно стать более подробным?объявить два глобальных класса
SomeSpecificCollection
иSomeSpecificCollectionElement
- которые имеют незначительные недостатки, но, вероятно, в порядке.объявить глобальный класс
SomeSpecificCollection
и классElement
как его вложенный класс. Затем:SomeSpecificCollection
вы ссылаетесь простоElement
и вездеSomeSpecificCollection::Element
- как - + выглядит так же, как 3., но более понятноSomeSpecificCollection
это тоже класс.На мой взгляд, последний вариант, безусловно, самый интуитивный и, следовательно, лучший дизайн.
Позвольте мне подчеркнуть - это не большая разница от создания двух глобальных классов с более подробными именами. Это просто крошечная деталь, но imho делает код более понятным.
Введение другой области видимости внутри класса
Это особенно полезно для введения typedefs или перечислений. Я просто выложу пример кода здесь:
Один тогда позвонит:
Но при рассмотрении предложений по дополнению кода
Product::
часто можно получить список всех возможных значений перечисления (BOX, FANCY, CRATE), и здесь легко ошибиться (строго типизированные перечисления C ++ 0x решают эту проблему, но не берите в голову) ).Но если вы введете дополнительную область видимости для этих перечислений, используя вложенные классы, вещи могут выглядеть следующим образом:
Тогда звонок выглядит так:
Затем, введя
Product::ProductType::
IDE, вы получите только перечисления из предложенной области действия. Это также снижает риск ошибки.Конечно, это может не понадобиться для небольших классов, но если у вас много перечислений, то это облегчает работу клиентских программистов.
Таким же образом, вы можете «организовать» большую группу typedef в шаблоне, если вам когда-нибудь понадобится. Это полезный шаблон иногда.
Идиома PIMPL
PIMPL (сокращение от Pointer to IMPLementation) - это идиома, полезная для удаления деталей реализации класса из заголовка. Это уменьшает необходимость перекомпиляции классов в зависимости от заголовка класса всякий раз, когда изменяется часть «реализации» заголовка.
Обычно это реализуется с использованием вложенного класса:
Xh:
x.cpp:
Это особенно полезно, если полное определение класса требует определения типов из некоторой внешней библиотеки, которая имеет тяжелый или просто уродливый заголовочный файл (например, WinAPI). Если вы используете PIMPL, тогда вы можете заключать любые специфичные для WinAPI функции только в
.cpp
и никогда не включать их в.h
.источник
struct Impl; std::auto_ptr<Impl> impl;
Эта ошибка была популяризирована Хербом Саттером. Не используйте auto_ptr для неполных типов или, по крайней мере, принимайте меры предосторожности, чтобы избежать генерирования неправильного кода.auto_ptr
неполный тип в большинстве реализаций, но технически это UB в отличие от некоторых шаблонов в C ++ 0x (напримерunique_ptr
), где было явно указано, что параметр шаблона может быть неполный тип и где именно тип должен быть завершен. (например, использование~unique_ptr
)T
изunique_ptr
может быть неполным типа.»enum class
.Я не часто использую вложенные классы, но я использую их время от времени. Особенно, когда я определяю какой-то тип данных и затем хочу определить функтор STL, разработанный для этого типа данных.
Например, рассмотрим универсальный
Field
класс, который имеет идентификационный номер, код типа и имя поля. Если я хочу выполнить поискvector
этихField
идентификаторов по номеру или имени, я мог бы создать функтор для этого:Тогда код, который должен искать эти
Field
s, может использоватьmatch
область действия внутри самогоField
класса:источник
Можно реализовать шаблон Builder с вложенным классом . Особенно в C ++, лично я нахожу это семантически чище. Например:
Скорее, чем:
источник