Solo funzioni senza una classe nel file cpp! buon design o no? [chiuso]

2

Quando stavo passando per il progetto su cui sto lavorando, mi sono imbattuto in un file cpp, che ha solo funzioni e variabili statiche. Mi chiedevo perché non hanno messo tutto in una classe! Ogni volta che è necessario chiamare le funzioni interne, il file cpp è incluso.

È un buon design? quali sono i pro e i contro di questo tipo di design?

per es.

//independent.cpp
#include "someclass.h"
static int var1;
static someclass object("hello");
static bool var2;

void foo(const char* dir, const char* filename){
...
}

somefile.cpp
#include "independent.cpp"
class sample{
public:
  void caller(){
  foo("c:\", "file.txt");
  }
}
    
posta ks2 17.05.2014 - 10:10
fonte

4 risposte

7

What are the pros and cons of these type of design?

Pro: facile da scrivere, facile da leggere, facile da chiamare.

Contro: conflitti di denominazione, rischio di variabili globali, difficile da ignorare.

I was wondering why they have not put everything into a class!

Se le tue funzioni non hanno bisogno di un contesto (ad esempio funzioni di utilità), avrai difficoltà a trovare un posto significativo per loro in una classe. Per quanto le classi siano un modo molto utile per organizzare codice e istanze, ci sono scenari in cui non valgono la pena.

Potresti anche obiettare che non è molto difficile metterlo in classe. Questo è un punto valido, ma gli autori non hanno visto alcun valore aggiunto.

Is this a good design?

I 100% sono d'accordo con l'interpretazione di Emilio Garavaglia su questo: dipende dal design buono o cattivo sul contesto. Non è sbagliato farlo in questo modo, ma potrebbe essere impossibile nell'ambito di alcuni progetti.

    
risposta data 17.05.2014 - 14:10
fonte
6

Semplicemente seguono un paradigma di oggetti singoli diverso dalla classe C ++ offerta:

le variabili statiche nel file CPP sono invisibili all'esterno, mentre un membro privato di una classe deve essere dichiarato nella classe (che deve essere indicato in un'intestazione).

Se vuoi rendere tali variabili irraggiungibili, l'accesso private è buono, ma se vuoi renderle secret , non possono essere "membri" di una classe la cui dimensione deve essere noto.

E se devono esistere solo una volta, una classe deve essere un singleton (ovvero una variabile nascosta statica accessibile da una funzione globale) che non è poi così diverso nel rendere la variabile stessa globale in un contesto molto limitato.

È solo la vecchia implementazione OOP basata su C che puoi trovare in Win32 o in GTK +.

Se il design è buono o cattivo, è più una questione di contesto che di idioma.

    
risposta data 17.05.2014 - 11:13
fonte
2

Uno dei motivi principali per cui non implementare un insieme di funzioni di utilità come classe è il fatto che quando si tratta di avere una classe senza stato che fornisce solo funzioni, allora c'è molto poco senso nell'istanziare quella classe come tutte le istanze sono identiche. Non c'è alcun significato nella definizione di un tipo di tipo di dati perché i tipi di dati sono contratti relativi a ciò che rappresentano lo stato / i dati. Quando non ci sono stati o dati, non c'è nulla da rappresentare.

Molte volte non è così semplice però. Per fare un esempio (questo è qualcosa che vorresti in una vera classe autosufficiente), considera una situazione in cui hai una funzione get_time_delta() e vuoi restituire il tempo trascorso dall'ultima chiamata di get_time_delta() funzione - una situazione comune quando si calcola per esempio quanto tempo ci vuole per eseguire una funzione render_scene() in un gioco. Dovresti memorizzare il tempo per la prima chiamata e il tempo per la seconda chiamata e sottrarre il primo dal secondo - ma senza stato questo è impossibile. Un modo per fare questo sarebbe implementare una classe e mantenere questo stato come variabili dei membri privati, ma se questa è una classe di utilità generica, quei dati temporali che sono rilevanti per get_time_delta() viene esposto anche ad altre funzioni di utilità. Questo è male perché si desidera ridurre al minimo l'ambito delle variabili. Un buon modo per procedere è quello di avere i dati di temporizzazione interni locali alla funzione get_time_delta() specificandola con static specificatori di classe di memoria.

Allo stato attuale, nell'esempio sopra citato, non abbiamo bisogno dello stato per la classe. Quindi, perché abbiamo bisogno di una classe per cominciare? Supponiamo di avere un'API come questa:

unsigned int delta_time = Util::get_time_delta();

Sarebbe irragionevole dover creare un'istanza di Util in quanto non contiene nessuno stato. Pertanto dichiariamo get_time_delta() come static funzioni della classe o dichiariamo get_time_delta() all'interno di uno spazio dei nomi Util .

Tutto ciò detto, "il modo C ++" (qualunque cosa significhi, non leggerlo troppo) è definire funzioni di utilità all'interno di uno spazio dei nomi invece che come% funzioni membro membro di una classe di utilità.

In altre lingue, in particolare in C # e Java, esiste un pattern chiamato modello di utilità che simula spazi dei nomi - posizionando funzioni di utilità dietro a un "namespace" dichiarandoli come funzioni membro statiche di una classe. In C ++ questo non è affatto necessario a causa degli spazi dei nomi incorporati e delle funzioni indipendenti.

    
risposta data 17.05.2014 - 22:40
fonte
0

La mia ipotesi è che sia usato per creare un gruppo di "eseguibili a singolo scopo" .

(Disclaimer: la mia conoscenza del C ++ non è buona come quella degli altri utenti - la mia risposta è solo un'ipotesi.)

Osserva che

  • Questo approccio consente di includere più classi riutilizzabili, "someclass.h", ecc. Quindi, si potrebbe dedurre che la famiglia di classi chiamate "someclass.h" segua effettivamente le buone pratiche OOP in C ++.

  • Tuttavia, se uno include più di un "independent.cpp" in una build, è probabile che si verifichi un conflitto di nomi. I conflitti di nomi effettivi potrebbero essere due variabili "statiche" con lo stesso nome ("var1") su due di tali file di origine (*) o potrebbero essere errori di collegamento.

    • (*) A meno che non vengano creati extern . Ricorda che queste righe sono incluse in un singolo file cpp .
  • Quindi, si potrebbe dedurre che in una build effettiva, solo un tale file "independent.cpp" potrebbe mai essere collegato per ogni eseguibile prodotto nella build. Se ci sono più file come questo, solo uno di questi potrebbe essere collegato alla volta.

(Dettagli.)

Supponiamo che esistano due file, "IndependentOne.cpp" e "IndependentTwo.cpp". Tieni presente che queste estensioni di file non sarebbero importanti se fossero incluse letteralmente in un altro file cpp .

Supponi che "IndependentOne.cpp" e "IndependentTwo.cpp" contengano una definizione per "var1":

// IndependentOne.cpp
#include "someClassOne.h"
static someClassOne var1;

// IndependentTwo.cpp
#include "someClassTwo.h"
static someClassTwo var1; 

// SomeMainFile.cpp
#include "IndependentOne.cpp"
#include "IndependentTwo.cpp"  // ---- compiler error here
int main(int argc, char** argv) 
{ ... }

Quindi var1 è definito in più modi. Per risolverlo, è necessario copiare e incollare manualmente i due file insieme, in "IndependentOneAndTwo.cpp" e rinominare le variabili statiche.

Quando è appropriato?

  • Ogni volta che è necessario "chiamare una funzione C ++ dalla riga di comando del sistema operativo",
  • Si desidera un file eseguibile per ogni funzione C ++ denominata
  • Nessun dato passato tra le chiamate; oppure i dati vengono sempre passati tramite il file system

Che diamine è questa filosofia?

Se non è pensato per essere utilizzato (nomi di file codificati, ecc.), quindi a cosa serve?

  • Throwaway "wrapper a riga di comando" per le funzioni C ++.
    • Ti permette di chiamare esattamente una funzione C ++;
    • Legge e scrive esattamente in un file o in una directory, il cui percorso è hard-coded.

Quali sono alcuni progetti alternativi?

  • Se il tuo obiettivo è wrapper della riga di comando throwaway , va bene.
    • Finché questi file sono esclusi da la build di produzione.
  • Se il tuo obiettivo è l'applicazione della riga di comando uso produzione ,
    • Implementare o utilizzare un'utilità di analisi della riga di comando decente o integrare un piccolo motore di scripting con le classi C ++ che rimuove la limitazione "call once and exit".
  • Se il tuo obiettivo è test del software (white-box / unit-testing) ,
    • Utilizza un framework di test delle unità.
risposta data 17.05.2014 - 23:43
fonte

Leggi altre domande sui tag