Domanda

Qualcuno mi ha parlato di una differenza di stile C++ nel proprio team.Ho il mio punto di vista sull'argomento, ma mi interesserebbe professionisti E contro proveniente da tutti.

Quindi, nel caso in cui tu abbia una proprietà di classe che vuoi esporre tramite due getter, uno di lettura/scrittura e l'altro di sola lettura (cioènon esiste un metodo fisso).Ci sono almeno due modi per farlo:

class T ;

class MethodA
{
   public :
      const T & get() const ;
      T & get() ;

      // etc.
} ;

class MethodB
{
   public :
      const T & getAsConst() const ;
      T & get() ;

      // etc.
} ;

Quali sarebbero i pro e i contro di ciascun metodo?

Mi interessano di più le ragioni tecniche/semantiche del C++, ma anche le ragioni di stile sono benvenute.

Notare che MethodB ha un grosso inconveniente tecnico (suggerimento:nel codice generico).

È stato utile?

Soluzione

Bene, per prima cosa, getAsConst dovere essere chiamato quando il puntatore "this" è const, non quando si desidera ricevere un oggetto const.Quindi, insieme a qualsiasi altra questione, ha un nome sottilmente errato.(Puoi ancora chiamarlo quando "questo" è non-const, ma non è né qui né lì.)

Ignorandolo, getAsConst non ti fa guadagnare nulla e impone un onere eccessivo allo sviluppatore che utilizza l'interfaccia.Invece di limitarsi a chiamare "get" e sapere che sta ottenendo ciò di cui ha bisogno, ora deve accertarsi se sta utilizzando o meno una variabile const e se il nuovo oggetto che sta prendendo deve essere const.E in seguito, se entrambi gli oggetti diventano non-const a causa di qualche refactoring, dovrà cambiare la sua chiamata.

Altri suggerimenti

Il C++ dovrebbe essere perfettamente in grado di gestire il metodo A in quasi tutte le situazioni.Lo uso sempre e non ho mai avuto problemi.

Il metodo B è, a mio avviso, un caso di violazione di OnceAndOnlyOnce.E ora devi capire se hai a che fare con const riferimento per scrivere il codice che viene compilato la prima volta.

Immagino che sia una questione stilistica: tecnicamente funzionano entrambi, ma MethodA rende il compilatore un po' più difficile da lavorare.Per me è una buona cosa.

Personalmente preferisco il primo metodo perché garantisce un'interfaccia più coerente.Inoltre, a me getAsConst() sembra altrettanto sciocco quanto getAsInt().

In una nota diversa, dovresti davvero pensarci due volte prima di restituire un riferimento non const o un puntatore non const a un membro dati della tua classe.Questo è un invito rivolto alle persone a sfruttare i meccanismi interni della tua classe, che idealmente dovrebbero essere nascosti.In altre parole rompe l'incapsulamento.Vorrei utilizzare get() const e set() e restituire un riferimento non const solo se non esiste altro modo o quando ha davvero senso, ad esempio fornire accesso in lettura/scrittura a un elemento di un array o una matrice.

Dato il precedente di stile stabilito dalla libreria standard (cioè Begin() e Begin() const per citare solo un esempio), dovrebbe essere ovvio che il metodo A è la scelta corretta.Metto in dubbio la sanità mentale della persona che sceglie il metodo B.

Quindi, il primo stile è generalmente preferibile.

Tuttavia utilizziamo parecchio una variazione del secondo stile nel codice base su cui sto attualmente lavorando, perché vogliamo una grande distinzione tra l'utilizzo const e non const.

Nel mio esempio specifico, abbiamo getTessellation e getMutableTessellation.È implementato con un puntatore copy-on-write.Per ragioni di prestazioni vogliamo che la versione const venga utilizzata ovunque possibile, quindi abbiamo accorciato il nome e gli abbiamo dato un nome diverso in modo che le persone non ne generino accidentalmente una copia quando non avrebbero comunque scritto.

Anche se sembra che la tua domanda riguardi solo un metodo, sarei felice di dare il mio contributo sullo stile.Personalmente, per ragioni di stile, preferisco la prima.La maggior parte degli IDE mostrerà la firma del tipo delle funzioni per te.

Preferirei il primo.Sembra migliore nel codice quando due cose che essenzialmente fanno la stessa cosa sembrano identiche.Inoltre, è raro che tu abbia un oggetto non const ma desideri chiamare il metodo const, quindi non è un grosso problema (e nel peggiore dei casi, avresti bisogno solo di un const_cast<>).

Il primo consente modifiche al tipo di variabile (se lo sia const o meno) senza ulteriore modifica del codice.Naturalmente, ciò significa che non viene inviata alcuna notifica allo sviluppatore che il percorso potrebbe essere cambiato rispetto a quello previsto.Quindi la questione è se apprezzi la possibilità di eseguire rapidamente il refactoring o avere una rete di sicurezza aggiuntiva.

Il secondo è qualcosa legato alla notazione ungherese che io personalmente NON in questo modo rimarrò con il Primo metodo.

Non mi piace la notazione ungherese perché aggiunge ridondanza che di solito detesto nella programmazione.È solo la mia opinione.

Dato che nascondi i nomi delle classi, questo spunto di riflessione sullo stile può essere applicabile o meno:

Ha senso dire a questi due oggetti, MetodoA e MetodoB, di "get" o "getAsConst"?Invieresti "get" o "getAsConst" come messaggi a uno degli oggetti?

Per come la vedo io, in quanto mittente del messaggio/invocatore del metodo, Voi sono quelli che ottengono il valore;quindi in risposta a questo messaggio "get", stai inviando un messaggio al Metodo A/Metodo B, il cui risultato è il valore che devi ottenere.

Esempio:Se il chiamante del MetodoA è, ad esempio, un servizio in SOA e il MetodoA è un repository, allora all'interno di get_A() del servizio, chiama MethodA.find_A_by_criteria(...).

Il principale svantaggio tecnologico del Metodo B che ho riscontrato è che quando si applica codice generico, è necessario raddoppiare il codice per gestire sia la versione const che quella non const.Per esempio:

Diciamo che T è un oggetto ordinabile (cioè possiamo confrontarlo con oggetti di tipo T con l'operatore <), e diciamo che vogliamo trovare il massimo tra due MethodA (resp.due metodi B).

Per il Metodo A, tutto ciò che dobbiamo codificare è:

template <typename T>
T & getMax(T & p_oLeft, T & p_oRight)
{
   if(p_oLeft.get() > p_oRight.get())
   {
      return p_oLeft ;
   }
   else
   {
      return p_oRight ;
   }
}

Questo codice funzionerà sia con oggetti const che con oggetti non const di tipo T:

// Ok
const MethodA oA_C0(), oA_C1() ;
const MethodA & oA_CResult = getMax(oA_C0, oA_C1) ;

// Ok again
MethodA oA_0(), oA_1() ;
MethodA & oA_Result = getMax(oA_0, oA_1) ;

Il problema sorge quando vogliamo applicare questo semplice codice a qualcosa seguendo la convenzione MethodB:

// NOT Ok
const MethodB oB_C0(), oB_C1() ;
const MethodB & oB_CResult = getMax(oB_C0, oB_C1) ; // Won't compile

// Ok
MethodA oB_0(), oB_1() ;
MethodA & oB_Result = getMax(oB_0, oB_1) ;

Affinché il MetodoB funzioni sia sulla versione const che su quella non const, dobbiamo utilizzare entrambi il getMax già definito, ma aggiungervi la seguente versione di getMax:

template <typename T>
const T & getMax(const T & p_oLeft, const T & p_oRight)
{
   if(p_oLeft.getAsConst() > p_oRight.getAsConst())
   {
      return p_oLeft ;
   }
   else
   {
      return p_oRight ;
   }
}

Conclusione, non fidandosi del compilatore sull'uso della costanza, ci carichiamo della creazione di due funzioni generiche quando una avrebbe dovuto essere sufficiente.

Naturalmente, con sufficiente paranoia, la seconda funzione del template avrebbe dovuto chiamarsi getMaxAsConst...E così il problema si propagherebbe a tutto il codice...

:-P

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