Le classi nidificate sono proprio come le classi normali, ma:
- hanno restrizioni di accesso aggiuntive (come fanno tutte le definizioni all'interno di una definizione di classe),
- che non inquinano il dato spazio dei nomi , ad esempio namespace globale. Se ritieni che la classe B sia così profondamente connessa alla classe A, ma gli oggetti di A e B non siano necessariamente correlati, allora potresti desiderare che la classe B sia accessibile solo tramite l'ambito della classe A (sarebbe indicata come A ::Classe).
Qualche esempio:
Classe di nidificazione pubblica per inserirlo in un ambito della classe pertinente
Supponiamo di voler avere una classe SomeSpecificCollection
che aggregi oggetti di classe Element
. È quindi possibile:
dichiarare due classi: SomeSpecificCollection
e Element
- cattivo, perché il nome "Elemento" è abbastanza generale da causare un possibile scontro tra nomi
introdurre uno spazio dei nomi someSpecificCollection
e dichiarare le classi someSpecificCollection::Collection
e someSpecificCollection::Element
. Nessun rischio di scontro tra nomi, ma può essere più dettagliato?
dichiarare due classi globali SomeSpecificCollection
e SomeSpecificCollectionElement
- che ha degli svantaggi minori, ma probabilmente va bene.
dichiarare la classe globale SomeSpecificCollection
e la classe Element
come classe nidificata. Poi:
- non rischi alcun conflitto di nomi poiché Element non si trova nello spazio dei nomi globale,
- in attuazione di
SomeSpecificCollection
te fai riferimento a just Element
, e ovunque come SomeSpecificCollection::Element
- che sembra + - uguale a 3., ma più chiaro
- diventa semplice che è "un elemento di una raccolta specifica", non "un elemento specifico di una raccolta"
- è visibile che
SomeSpecificCollection
è anche una classe.
Secondo me, l'ultima variante è sicuramente il design più intuitivo e quindi il migliore.
Consentitemi di sottolineare: non è una grande differenza rispetto alla creazione di due classi globali con nomi più dettagliati. È solo un piccolo dettaglio, ma lo rende più chiaro.
Presentazione di un altro ambito all'interno di un ambito di classe
Ciò è particolarmente utile per introdurre typedef o enumerazioni. Pubblicherò solo un esempio di codice qui:
class Product {
public:
enum ProductType {
FANCY, AWESOME, USEFUL
};
enum ProductBoxType {
BOX, BAG, CRATE
};
Product(ProductType t, ProductBoxType b, String name);
// the rest of the class: fields, methods
};
Uno quindi chiamerà:
Product p(Product::FANCY, Product::BOX);
Ma quando si esaminano le proposte di completamento del codice Product::
, si ottengono spesso tutti i possibili valori enum (BOX, FANCY, CRATE) elencati ed è facile fare un errore qui (gli enum fortemente tipizzati di C ++ 0x risolvono questo problema, ma non importa ).
Ma se si introduce un ambito aggiuntivo per quegli enum usando le classi nidificate, le cose potrebbero apparire come:
class Product {
public:
struct ProductType {
enum Enum { FANCY, AWESOME, USEFUL };
};
struct ProductBoxType {
enum Enum { BOX, BAG, CRATE };
};
Product(ProductType::Enum t, ProductBoxType::Enum b, String name);
// the rest of the class: fields, methods
};
Quindi la chiamata appare come:
Product p(Product::ProductType::FANCY, Product::ProductBoxType::BOX);
Quindi digitando Product::ProductType::
un IDE, si otterranno solo gli enumerazione dall'ambito desiderato suggerito. Ciò riduce anche il rischio di fare un errore.
Naturalmente questo potrebbe non essere necessario per le piccole classi, ma se uno ha molti enumerazioni, allora rende le cose più facili per i programmatori client.
Allo stesso modo, potresti "organizzare" un grande gruppo di dattiloscritti in un modello, se mai ne avessi bisogno. È un modello utile a volte.
Il linguaggio di PIMPL
PIMPL (abbreviazione di Pointer to IMPLementation) è un linguaggio utile per rimuovere dall'intestazione i dettagli di implementazione di una classe. Ciò riduce la necessità di ricompilare le classi a seconda dell'intestazione della classe ogni volta che la parte "implementazione" dell'intestazione cambia.
Di solito è implementato usando una classe nidificata:
Xh:
class X {
public:
X();
virtual ~X();
void publicInterface();
void publicInterface2();
private:
struct Impl;
std::unique_ptr<Impl> impl;
}
X.cpp:
#include "X.h"
#include <windows.h>
struct X::Impl {
HWND hWnd; // this field is a part of the class, but no need to include windows.h in header
// all private fields, methods go here
void privateMethod(HWND wnd);
void privateMethod();
};
X::X() : impl(new Impl()) {
// ...
}
// and the rest of definitions go here
Ciò è particolarmente utile se la definizione di classe completa necessita della definizione di tipi da una libreria esterna che ha un file di intestazione pesante o semplicemente brutto (prendi WinAPI). Se si utilizza PIMPL, è possibile racchiudere qualsiasi funzionalità specifica di WinAPI solo in .cpp
e non includerla mai .h
.