Alguém pode me indicar alguns recursos interessantes para entender e usar classes aninhadas? Eu tenho alguns materiais como Princípios de Programação e coisas como este IBM Knowledge Center - Nested Classes
Mas ainda estou tendo problemas para entender o propósito deles. Alguém poderia me ajudar, por favor?
c++
nested
inner-classes
com óculos
fonte
fonte
typedef
. 3. porque adicionam um nível adicional de recuo em um ambiente onde evitando longas filas já é difícil 4. porque você está declarando dois objetos conceitualmente separadas em uma únicaclass
declaração, etc.Respostas:
Classes aninhadas são legais para ocultar detalhes de implementação.
Lista:
Aqui, não quero expor o Node, pois outras pessoas podem decidir usar a classe e isso me impediria de atualizar minha classe, pois qualquer coisa exposta faz parte da API pública e deve ser mantida para sempre . Ao tornar a classe privada, não apenas oculto a implementação, mas também digo que é minha e posso alterá-la a qualquer momento para que você não possa usá-la.
Veja
std::list
oustd::map
todos eles contêm classes ocultas (ou não?). A questão é que eles podem ou não, mas como a implementação é privada e oculta, os construtores da STL conseguiram atualizar o código sem afetar a maneira como você o utilizava, ou deixando muitas bagagens antigas em volta da STL porque precisam para manter a compatibilidade com versões anteriores de algum tolo que decidiu que queria usar a classe Node que estava ocultalist
.fonte
Node
não deverá ser exposto no arquivo de cabeçalho.detail
convenção: em vez disso, dependendo de tais convenções, é preciso ter em mente a si mesmo, é melhor depender do compilador que as controla para você.Classes aninhadas são como classes regulares, mas:
Alguns exemplos:
Aninhando publicamente classe para colocá-la em um escopo de classe relevante
Suponha que você queira ter uma classe
SomeSpecificCollection
que agregue objetos da classeElement
. Você pode:declare duas classes:
SomeSpecificCollection
eElement
- ruim, porque o nome "Elemento" é geral o suficiente para causar um possível conflito de nomeintroduza um espaço para nome
someSpecificCollection
e declare classessomeSpecificCollection::Collection
esomeSpecificCollection::Element
. Não há risco de conflito de nomes, mas pode ficar mais detalhado?declarar duas classes globais
SomeSpecificCollection
eSomeSpecificCollectionElement
- que tem desvantagens menores, mas provavelmente está OK.declarar classe global
SomeSpecificCollection
e classeElement
como sua classe aninhada. Então:SomeSpecificCollection
você se refere apenasElement
, e em todos os outros lugares, comoSomeSpecificCollection::Element
- que parece + - o mesmo que 3., mas mais claroSomeSpecificCollection
também é uma classe.Na minha opinião, a última variante é definitivamente o design mais intuitivo e, portanto, o melhor.
Deixe-me enfatizar: não é uma grande diferença criar duas classes globais com nomes mais detalhados. É apenas um pequeno detalhe, mas imho torna o código mais claro.
Introduzindo outro escopo dentro de um escopo de classe
Isso é especialmente útil para a introdução de typedefs ou enumerações. Vou apenas postar um exemplo de código aqui:
Um então chamará:
Mas, ao analisar as propostas de conclusão de código para
Product::
, geralmente é possível obter todos os valores possíveis de enumeração (BOX, FANCY, CRATE) e é fácil cometer um erro aqui (as enumerações fortemente tipadas do C ++ 0x resolvem isso, mas não importa) )Mas se você introduzir um escopo adicional para essas enumerações usando classes aninhadas, as coisas podem se parecer com:
Em seguida, a chamada se parece com:
Ao digitar
Product::ProductType::
um IDE, obteremos apenas as enumerações do escopo desejado sugerido. Isso também reduz o risco de cometer um erro.É claro que isso pode não ser necessário para turmas pequenas, mas se houver muitas enumerações, isso facilitará as coisas para os programadores clientes.
Da mesma forma, você pode "organizar" um grande número de typedefs em um modelo, se você precisar. Às vezes é um padrão útil.
O idioma do PIMPL
O PIMPL (abreviação de Pointer to IMPLementation) é um idioma útil para remover os detalhes de implementação de uma classe do cabeçalho. Isso reduz a necessidade de recompilar classes, dependendo do cabeçalho da classe sempre que a parte "implementação" do cabeçalho é alterada.
Geralmente é implementado usando uma classe aninhada:
Xh:
X.cpp:
Isso é particularmente útil se a definição de classe completa precisar da definição de tipos de alguma biblioteca externa que tenha um arquivo de cabeçalho pesado ou apenas feio (use o WinAPI). Se você usar o PIMPL, poderá incluir qualquer funcionalidade específica do WinAPI apenas
.cpp
e nunca incluí-la.h
.fonte
struct Impl; std::auto_ptr<Impl> impl;
Este erro foi popularizado por Herb Sutter. Não use auto_ptr em tipos incompletos, ou pelo menos tome precauções para evitar que código errado seja gerado.auto_ptr
tipo incompleto na maioria das implementações, mas tecnicamente é UB diferente de alguns dos modelos em C ++ 0x (por exemplounique_ptr
), onde foi explicitado que o parâmetro do modelo pode ser um tipo incompleto e onde exatamente o tipo deve estar completo. (por exemplo, uso de~unique_ptr
)T
do modelo deunique_ptr
pode ser um tipo incompleto."enum class
.Não uso muito classes aninhadas, mas uso-as de vez em quando. Especialmente quando eu defino algum tipo de tipo de dados e, em seguida, desejo definir um functor STL projetado para esse tipo de dados.
Por exemplo, considere uma
Field
classe genérica que tenha um número de identificação, um código de tipo e um nome de campo. Se eu quiser procurar umvector
dessesField
s por número de identificação ou nome, posso construir um functor para fazer isso:Em seguida, o código que precisa procurar esses
Field
s pode usar omatch
escopo dentro daField
própria classe:fonte
Pode-se implementar um padrão Builder com classe aninhada . Especialmente em C ++, pessoalmente acho semanticamente mais limpo. Por exemplo:
Ao invés de:
fonte