Domanda

Quali sono i pro e i contro dell'utilizzo di classi ed enumerazioni C ++ pubbliche nidificate? Ad esempio, supponiamo di avere una classe chiamata printer e che questa classe memorizzi anche informazioni sui vassoi di uscita, si potrebbe avere:

class printer
{
public:
    std::string name_;

    enum TYPE
    {
        TYPE_LOCAL,
        TYPE_NETWORK,
    };

    class output_tray
    {
        ...
    };
    ...
};

printer prn;
printer::TYPE type;
printer::output_tray tray;

In alternativa:

class printer
{
public:
    std::string name_;
    ...
};

enum PRINTER_TYPE
{
    PRINTER_TYPE_LOCAL,
    PRINTER_TYPE_NETWORK,
};

class output_tray
{
    ...
};

printer prn;
PRINTER_TYPE type;
output_tray tray;

Riesco a vedere i vantaggi della nidificazione di enumerazioni / classi private, ma quando si tratta di quelle pubbliche, l'ufficio è diviso - sembra essere più una scelta di stile.

Quindi, quale preferisci e perché?

È stato utile?

Soluzione

Classi nidificate

Ci sono molti effetti collaterali nelle classi nidificate all'interno delle classi che di solito considero difetti (se non antipattern puri).

Immaginiamo il seguente codice:

class A
{
   public :
      class B { /* etc. */ } ;

   // etc.
} ;

O anche:

class A
{
   public :
      class B ;

   // etc.
} ;

class A::B
{
   public :

   // etc.
} ;

  • Accesso privilegiato: A :: B ha accesso privilegiato a tutti i membri di A (metodi, variabili, simboli, ecc.), che indebolisce l'incapsulamento
  • L'ambito di A è candidato per la ricerca di simboli: il codice all'interno di B vedrà tutti simboli di A come possibili candidati per una ricerca di simboli, che può confondere il codice
  • dichiarazione a termine: non è possibile inoltrare una dichiarazione A :: B senza fornire una dichiarazione completa di A
  • Estensibilità: È impossibile aggiungere un'altra classe A :: C a meno che tu non sia il proprietario di A
  • Verbicità del codice: l'inserimento di classi in classi rende solo le intestazioni più grandi. Puoi ancora separarlo in più dichiarazioni, ma non c'è modo di usare alias, importazioni o usi simili a spazi dei nomi.

Come conclusione, salvo eccezioni (ad es. la classe nidificata è una parte intima della classe di nidificazione ... E anche allora ...), non vedo alcun punto nelle classi nidificate nel codice normale, poiché i difetti superano per grandezza i vantaggi percepiti.

Inoltre, ha l'odore del maldestro tentativo di simulare lo spazio dei nomi senza usare gli spazi dei nomi C ++.

Dal punto di vista professionale, isola questo codice e, se privato, lo rendi inutilizzabile ma dall'esterno di "quotazione" Classe ...

Enumer nidificati

Pro: tutto.

Con: Nothing.

Il fatto è che gli elementi enum inquineranno l'ambito globale:

// collision
enum Value { empty = 7, undefined, defined } ;
enum Glass { empty = 42, half, full } ;

// empty is from Value or Glass?

L'unione di ogni enum in uno spazio dei nomi / classe diverso ti consentirà di evitare questa collisione:

namespace Value { enum type { empty = 7, undefined, defined } ; }
namespace Glass { enum type { empty = 42, half, full } ; }

// Value::type e = Value::empty ;
// Glass::type f = Glass::empty ;

Nota che C ++ 0x ha definito la classe enum:

enum class Value { empty, undefined, defined } ;
enum class Glass { empty, half, full } ;

// Value e = Value::empty ;
// Glass f = Glass::empty ;

esattamente per questo tipo di problemi.

Altri suggerimenti

Uno svantaggio che può diventare un grosso problema per i grandi progetti è che è impossibile fare una dichiarazione in avanti per le classi o le enumerazioni nidificate.

Se non userete mai la classe dipendente per altro che lavorare con le implementazioni della classe indipendente, secondo me le classi nidificate vanno bene.

È quando vuoi usare il " internal " classe come un oggetto a sé stante che le cose possono iniziare a diventare un po 'traballanti e devi iniziare a scrivere routine di estrattore / inseritore. Non è una situazione carina.

Sembra che dovresti usare gli spazi dei nomi anziché le classi per raggruppare come cose correlate in questo modo. Uno svantaggio che ho potuto vedere durante le lezioni nidificate è che si finisce con un file sorgente molto grande che potrebbe essere difficile da individuare quando si cerca una sezione.

Non ci sono pro e contro di per sé nell'uso di classi C ++ pubbliche nidificate. Ci sono solo fatti. Tali fatti sono obbligati dallo standard C ++. Il fatto che un fatto sulle classi C ++ pubbliche nidificate sia un pro o un contro dipende dal particolare problema che si sta tentando di risolvere. L'esempio che hai fornito non consente di valutare se le classi nidificate siano appropriate o meno.

Un fatto sulle classi nidificate è che hanno un accesso privilegiato a tutti i membri della classe a cui appartengono. Questo è un contro, se le classi nidificate non necessitano di tale accesso. Ma se la classe nidificata non ha bisogno di tale accesso, non avrebbe dovuto essere dichiarata come classe nidificata. Ci sono situazioni in cui una classe A vuole concedere un accesso privilegiato ad alcune altre classi B . Esistono tre soluzioni a questo problema

  1. Rendi B amico di A
  2. Rendi B una classe nidificata di A
  3. Rendi i metodi e gli attributi di cui B ha bisogno, i membri pubblici di A .

In questa situazione, è il n. 3 che viola l'incapsulamento, perché A ha il controllo sui suoi amici e sulle sue classi nidificate, ma non su quelle che chiamano i suoi metodi pubblici o accedono ai suoi attributi pubblici.

/ p>

Un altro fatto sulle classi nidificate è che è impossibile aggiungere un'altra classe A :: C come classe nidificata di A a meno che tu non sia il proprietario di A . Tuttavia, questo è perfettamente ragionevole, perché le classi nidificate hanno accesso privilegiato. Se fosse possibile aggiungere A :: C come classe nidificata di A , allora A :: C potrebbe ingannare A nel garantire l'accesso a informazioni privilegiate; e che potresti violare l'incapsulamento. È praticamente lo stesso della dichiarazione friend : la dichiarazione friend non ti concede alcun privilegio speciale, che il tuo amico si nasconde agli altri; consente ai tuoi amici di accedere alle informazioni che nascondi ai tuoi non amici. In C ++, chiamare qualcuno amico è un atto altruistico, non egoistico. Lo stesso vale per consentire a una classe di essere una classe nidificata.

Som altri fatti su classi pubbliche nidificate:

  • L'ambito di A è candidato per la ricerca di simboli di B : se non vuoi questo, fai di B un amico di A invece di una classe nidificata. Tuttavia, ci sono casi in cui si desidera esattamente questo tipo di ricerca di simboli.
  • A::B non può essere dichiarato in avanti : A e A :: B sono strettamente accoppiati. Essere in grado di usare A :: B senza sapere A nasconderebbe solo questo fatto.

Per riassumere questo: se lo strumento non soddisfa le tue esigenze, non dare la colpa allo strumento; incolpare te stesso per l'utilizzo dello strumento; altri potrebbero avere problemi diversi, per i quali lo strumento è perfetto.

paercebal ha detto tutto ciò che direi degli enumerati nidificati.

Classi nidificate WRT, il mio caso d'uso comune e quasi unico per loro è quando ho una classe che sta manipolando un tipo specifico di risorsa e ho bisogno di una classe di dati che rappresenti qualcosa di specifico per quella risorsa. Nel tuo caso, output_tray potrebbe essere un buon esempio, ma in genere non uso classi nidificate se la classe avrà metodi che verranno chiamati dall'esterno della classe contenente o è più che principalmente una classe di dati. In genere, inoltre, non annido classi di dati a meno che la classe contenuta non venga mai direttamente referenziata al di fuori della classe contenitiva.

Quindi, per esempio, se avessi una classe printer_manipulator, potrebbe avere una classe contenuta per errori di manipolazione della stampante, ma la stessa stampante sarebbe una classe non contenuta.

Spero che questo aiuti. :)

Ricorda che puoi sempre promuovere una classe nidificata a una di livello superiore in un secondo momento, ma potresti non essere in grado di fare il contrario senza rompere il codice esistente. Pertanto, il mio consiglio sarebbe di renderlo prima una classe nidificata e, se inizia a diventare un problema, renderlo una classe di livello superiore nella prossima versione.

Per me un grande svantaggio di averlo fuori è che diventa parte dello spazio dei nomi globale. Se l'enum o la classe correlata si applica solo alla classe in cui si trova, allora ha senso. Quindi, nel caso della stampante, tutto ciò che include la stampante saprà di avere pieno accesso all'enum PRINTER_TYPE, dove non è necessario conoscerlo. Non posso dire di aver mai usato una classe interna, ma per un enum, sembra più logico tenerlo dentro. Come ha sottolineato un altro poster, è anche una buona idea usare gli spazi dei nomi per raggruppare oggetti simili, dal momento che ostruire lo spazio dei nomi globale può davvero essere una brutta cosa. In precedenza ho lavorato a progetti che sono enormi e solo per far apparire un elenco di completamento automatico nello spazio dei nomi globale ci vogliono 20 minuti. A mio avviso, enumerazioni nidificate e classi / strutture basate sul nome sono probabilmente l'approccio più pulito.

Sono d'accordo con i post che invocano l'incorporamento del tuo enum in una classe ma ci sono casi in cui ha più senso non farlo (ma per favore, almeno inseriscilo in uno spazio dei nomi). Se più classi utilizzano un enum definito all'interno di una classe diversa, tali classi dipendono direttamente dall'altra classe concreta (che possiede l'enum). Ciò rappresenta sicuramente un difetto di progettazione poiché quella classe sarà responsabile di quell'enum e di altre responsabilità.

Quindi, sì, incorpora l'enum in una classe se altro codice usa quell'enum solo per interfacciarsi direttamente con quella classe concreta. Altrimenti, trova un posto migliore per mantenere l'enum come uno spazio dei nomi.

Se metti l'enum in una classe o in uno spazio dei nomi, intellisense sarà in grado di darti una guida quando stai cercando di ricordare i nomi dell'enum. Una piccola cosa è certa, ma a volte le piccole cose contano.

Visual Studio 2008 non sembra essere in grado di fornire intellisense per le classi nidificate, quindi sono passato al linguaggio PIMPL nella maggior parte dei casi in cui avevo una classe nidificata. Metto sempre enum nella classe se viene usato solo da quella classe, o al di fuori della classe nello stesso spazio dei nomi della classe quando più di una classe usa l'enum.

Riesco a vedere un truffatore per le classi nidificate, che si potrebbe usare meglio la programmazione generica.

Se la classe piccola viene definita al di fuori di quella grande, puoi rendere la classe grande un modello di classe e usare qualsiasi "piccola" quotazione classe di cui potresti aver bisogno in futuro con la grande classe.

La programmazione generica è uno strumento potente e, IMHO, dovremmo tenerlo presente durante lo sviluppo di programmi estensibili. Strano che nessuno abbia menzionato questo punto.

L'unico problema con le classi nidificate in cui mi sono imbattuto ancora era che C ++ non ci consente di fare riferimento all'oggetto della classe che racchiude, nelle funzioni della classe nidificata. Non possiamo dire " Allegando :: this "

(Ma forse c'è un modo?)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top