2012-03-22 6 views
7

Ecco una domanda che si presenta ancora e ancora nell'implementazione della classe C++. Sono curioso di sapere quali sono i pensieri delle persone qui. Quale codice preferisci e perché?Funzioni private C++: se passare la variabile membro della classe in base al parametro funzione, oppure no

class A 
{ 
public: 
    /* Constructors, Destructors, Public interface functions, etc. */ 
    void publicCall(void); 

private: 
    void f(void); 

    CMyClass m_Member1; 
}; 

con

void A::publicCall(void) 
{ 
    f(); 
} 

void A::f(void) 
{ 
    // do some stuff populating m_Member1 
} 

Oppure l'alternativa:

class A 
{ 
public: 
    /* Constructors, Destructors, Public interface functions, etc. */ 
    void publicCall(void); 

private: 
    void f(CMyClass &x); 

    CMyClass m_Member1; 
}; 

con

void A::publicCall(void) 
{ 
    f(m_Member1); 
} 

void A::f(CMyClass &x) 
{ 
    // do some stuff to populate x, 
    // locally masking the fact that it's really m_Member1 
} 

Credo che preferisco sempre la seconda perché poi f può quindi operare su qualsiasi istanza di CMyClass ma, detto questo, ho un sacco di codice in cui il primo è perfettamente valido dal f funzionerà sempre su m_Member1 e lo sto davvero suddividendo in due funzioni per rendere il codice più leggibile.

Sì, questa è più una domanda di discussione che una domanda di "risposta", ma sono più interessato al ragionamento. Contrassegnerò come risposta una risposta che dia un buon ragionamento o un buon criterio.

Inoltre, tieni presente che questo è solo un esempio di giocattolo. La classe sarà più grande di questa realtà e quindi l'organizzazione è importante.

+1

Nel secondo caso, è possibile rendere 'f' statico e creare codice quasi identico al primo caso. –

+1

@Kerrek SB, questo è vero ed è un ottimo punto. In realtà, non c'è ragione per cui f() 'deve essere anche un membro di questa classe. Potrebbe essere la sua funzione di utilità autonoma. Basti dire per ora che 'f()' non è particolarmente utile se non nel contesto di questa classe. –

+0

perché il primo f non può funzionare su qualsiasi istanza di CMyClass? – aib

risposta

1

Chiediti: ha qualche significato ora o può avere un significato nel futuro prevedibile, per chiamare f() con un oggetto diverso da m_Member1?

Se la risposta è:

  • No. Un valore m_Member1 senza parametro è una parte intrinseca di A.
  • . Fai il f(CMyClass &). Anche se ora usi solo m_Member1, questa non è una proprietà intrinseca delle classi che gestisci.
  • Forse. Beh ... direi di andare con l'f() senza parametri. C'è sempre la possibilità di cambiare idea (il cambiamento se abbastanza banale, in realtà).

Si noti inoltre che una funzione f() può chiamare un'altra funzione g(CMyClass &), ma non viceversa. Quindi, a seconda di cosa fa f(), ciò potrebbe limitare le tue opzioni.

+0

+1 , ma metterò in discussione alcune parti di ciò che hai detto nello spirito di un sano dibattito (principalmente perché voglio delle ragioni): se la risposta alla tua domanda è "no", allora perché preferiresti "f()"? Inoltre, perché sembri preferire il parametroless 'f()' sull'altro? Immagino che le tue ultime due frasi diano qualche ragionamento qui ... –

+0

Primo, perché se 'm_Member1' è considerato una parte unica di' A' (ad esempio, 'Head' fa parte di' Body') quindi 'f()' può essere interpretato come un'operazione su 'A', anche se è necessario toccare il varaible di 'm_Member1' (' Body :: pickYourNose() 'non ha bisogno di un riferimento al tuo' Head' o al tuo 'Naso'). Secondo: perché se poi scrivi un 'g (CMyClass &)' che ha bisogno di chiamare 'f()' avrai ora che hai sbagliato! – rodrigo

+0

Inoltre, avere due riferimenti allo stesso oggetto nello stesso posto ('member1' e' m_Member1') senza una ragione non è probabilmente una buona idea. – rodrigo

3

Dato che chiedi opinioni, se una funzione autonoma f(CMyClass&) ha senso ed è implementabile, allora vorrei anche favorire questa opzione. Opterei per il primo caso se l'operazione eseguita da f ha senso solo nel contesto della classe A, se CMyClass ha senso solo nel contesto di A, o se dipende da altri attributi di A. Penso che si debba decidere a seconda del problema.

+0

"Opterei per il secondo caso se l'operazione eseguita da f ha senso solo nel contesto della classe A." Intendevi dire "il primo caso"? –

+0

@ChrisA Sì, in effetti intendevo il primo caso! Lo aggiusterò. – juanchopanza

1

Non è f nel secondo esempio meglio di una funzione statica di A o funzione globale o, forse, una funzione membro di CMyClass?

Ovviamente, ci sono casi in cui è meglio inviare l'argomento ogni volta che si chiama tale funzione, ma perché dovresti inviarlo di nuovo quando hai già un oggetto CMyClass nell'oggetto A. Se è necessario interagire con entrambi gli oggetti CMyClass, è preferibile aggiungerlo all'elenco delle funzioni membro CMyClass anziché a A.

Inoltre, come Clean Code stati, è meglio con funzioni senza argomenti, che con quelli con argomenti. Quando un altro programmatore tenta di leggere la funzione, deve decifrare/prestare attenzione al secondo parametro, oltre al nome della funzione.

+0

+1 Nizza punti. –

1

Vorrei basare la risposta sul contesto. Se ci sono ora o potrebbero essere in qualche giorno più istanze di variabili membro su cui possa operare f, quindi sicuro, passarle/loro come parametri. Tuttavia, se f sta operando su elementi specifici dello stato dell'istanza di A, non vorrei passare nulla ad esso. Ci sono molti casi in cui ci sarà sempre un solo foo in una A. A quel punto diventa sciocco rendere foo un parametro a f. E un po 'meno efficiente, a meno che f non sia in linea, poiché non solo viene passato questo puntatore, ma anche l'indirizzo di foo, che è una copia extra nello stack.

2

Come sempre, dipende. Ogni scenario è diverso.

Nell'esempio specifico che hai fornito - in primo luogo escluderei la tua alternativa (void A::f(CMyClass &x)) principalmente perché "odora" male (come dice Martin Fowler). È una funzione privata e, a meno che non sia necessario utilizzarlo, ora per altre istanze, utilizzarlo. Puoi sempre refactoring in caso di necessità.

Immaginate cosa succederebbe se f avesse 2 parametri. 3 parametri. 10. Avrebbe senso quindi inviarli ogni volta? Non sarebbe meglio avere questi parametri membri?

E cosa succede se f ha dovuto inviare alcuni di questi argomenti ad altri metodi di A? Non ha più senso usare i membri per questo?

Tutto ciò presuppone che f abbia bisogno di altre informazioni che contiene A, altrimenti lo sposterei come metodo CMyClass.

+0

+1, mi piace il ragionamento qui. La cosa con parametro 2,3,10 è particolarmente rilevante per la mia classe man mano che aumenta. –