È buono usare le funzioni private dei membri void in C ++?

0

Attualmente sto scrivendo una classe che esegue un calcolo piuttosto complesso che dovrebbe essere attivato da una chiamata di funzione.

I passi per usare la classe sarebbero:

  1. Costruzione che include l'inizializzazione dei parametri di base
  2. Inizializzazione con alcune altre impostazioni specifiche del caso
  3. Il calcolo attuale
  4. Ora, l'utente della classe può recuperare gli output che sono memorizzati come variabili di classe.

Il passaggio 2 è separato dal passaggio 1 in modo da poter eseguire più calcoli con gli stessi parametri semplicemente cambiando quelli specifici del caso invece di creare un nuovo oggetto.

Il grande calcolo deve ora essere separato in diverse funzioni ausiliarie. Il mio approccio per questo sta utilizzando le funzioni private void che gestiscono solo le variabili di classe, ad es.

class MyComputation {
 public:
  MyComputation(/* some basic parameters */);
  void initSpecificCase(/* some case specific parameters */);
  void doTheActualComputation();
  Results getResults();

 private:
  void computeThisAndThat();
  void computeSomeInterimResults();

  // Inputs, outputs, interim results
};

dove doTheActualComputation chiama computeThisAndThat e computeSomeInterimResults .

Questo uso delle funzioni private è una buona pratica? O dovrei piuttosto mettere tutte le cose ausiliarie in uno spazio dei nomi anonimo? Ciò comporterebbe il passaggio di molti argomenti alle funzioni nello spazio dei nomi anonimo, anche alcuni elementi complessi del contenitore.

Si prega di notare che questa domanda non riguarda il passare argomenti ma piuttosto l'uso di void funzioni membro private con l'assunto che i citati risultati intermedi hanno senso come variabili di classe che descrivono effettivamente stato dell'oggetto classe.

    
posta Gregor 21.10.2017 - 11:52
fonte

1 risposta

6

Scomporre il codice in funzioni di membri privati è completamente OK. Di per sé, un tipo di ritorno vuoto non è un problema: la funzione può avere un effetto esterno o può cambiare lo stato della classe.

Ma questo cambiamento di stato potrebbe essere un problema. L'oggetto memorizza sia lo stato generale che viene utilizzato su più calcoli (e che probabilmente non dovrebbe cambiare), ma anche lo stato per problema temporaneo. Cosa succede allora il calcolo genera un'eccezione? È spesso difficile assicurarsi che l'oggetto sia sempre in uno stato valido in grado di eseguire il calcolo successivo quando viene riutilizzato in questo modo.

Spesso è un progetto migliore separare questi diversi tipi di dati. I risultati intermedi per computazione possono essere archiviati in un oggetto che non è esternamente visibile e può essere eliminato successivamente. Ciò semplifica la gestione del ciclo di vita.

Nell'intestazione, definiamo una classe che memorizza i parametri di base e può eseguire il calcolo:

/** Usage:
 *      MyComputation comp(basic_params...);
 *      ...
 *      Results r = comp(specific_params...);
 */
class MyComputation {
public:
  MyComputation(BasicParams...);
  ~MyComputation();

  // Could also be a named method, doesn't matter.
  Results operator()(SpecificParams...) const;

private:
  BasicParams... m_basic_params;
};

Nel codice sorgente attuale, possiamo ora definire il calcolo, e soprattutto: strutture dati per i parametri per computazione. Possiamo utilizzare funzioni statiche o utilizzare uno spazio dei nomi anonimo per limitarne la visibilità (tecnicamente: per garantire il collegamento interno):

#include "MyComputation.h"

MyComputation::MyComputation(BasicParams...) ... { ... }
MyComputation::~MyComputation() { ... }

namespace {

struct Params {
  BasicParams const& basic;  // borrow from MyComputation
  SpecificParams params;
  InterimResults results;
  ...
};

void doSomethingInteresting(Params& p) { ... }
InterimResults canReturnOrMutateWhateverYouWant(SpecificParams& p) { ... }
Results extractResults(Params p) { ... }

}  // end anonymous namespace

Results MyComputation::operator()(SpecificParams specific_params) const
{
   Params params { m_basic_params, specific_params, {} };
   doSomethingInteresting(params);
   params.results = canReturnOrMutateWhateverYouWant(params.params);
   return extractResults(std::move(params));
}

All'interno di questo file sei libero di utilizzare le funzioni membro o non membro come è conveniente. Le funzioni non membro sono utilizzabili qui perché la maggior parte dei parametri sono già raggruppati in un singolo oggetto.

Non devi perdere la struttura interna nell'intestazione (tranne ovviamente se si tratta di codice basato su modelli). Puoi definire qualsiasi struttura di dati che ti serve. Poiché tutto qui è effettivamente privato, non devi specificare esplicitamente private visibilità.

    
risposta data 21.10.2017 - 12:22
fonte

Leggi altre domande sui tag