È una cattiva idea aver creato un metodo di classe che passa variabili di classe?


14

Ecco cosa intendo:

class MyClass {
    int arr1[100];
    int arr2[100];
    int len = 100;

    void add(int* x1, int* x2, int size) {
        for (int i = 0; i < size; i++) {
            x1[i] += x2[i];
        }
    }
};

int main() {
    MyClass myInstance;

    // Fill the arrays...

    myInstance.add(myInstance.arr1, myInstance.arr2, myInstance.len);
}

addpuò già accedere a tutte le variabili di cui ha bisogno, dal momento che è un metodo di classe, quindi è una cattiva idea? Ci sono ragioni per cui dovrei o non dovrei farlo?


13
Se hai voglia di farlo, accenna a un cattivo design. Le proprietà della classe probabilmente appartengono altrove.
bitsoflogic,

11
Lo snippet è troppo piccolo. Questo livello di astrazioni miste non si verifica in frammenti di queste dimensioni. Puoi anche risolvere questo problema con del buon testo su considerazioni di progettazione.
Giosuè, il

16
Onestamente non capisco perché dovresti farlo. Cosa guadagni? Perché non avere semplicemente un addmetodo no-arg che opera direttamente sui suoi interni? Solo perché?
Ian Kemp il

2
@IanKemp O hai un addmetodo che accetta argomenti ma non esiste come parte di una classe. Solo una pura funzione per aggiungere due array insieme.
Kevin,

Il metodo add aggiunge sempre arr1 e arr2 o può essere usato per aggiungere qualcosa a qualcosa?
user253751

Risposte:


33

Ci potrebbero essere cose con la classe che farei diversamente, ma per rispondere alla domanda diretta, la mia risposta sarebbe

si, è una cattiva idea

Il motivo principale di ciò è che non hai alcun controllo su ciò che viene passato alla addfunzione. Sicuramente speri che sia uno degli array membri, ma cosa succede se qualcuno passa in un array diverso che ha dimensioni inferiori a 100 o se passi in una lunghezza maggiore di 100?

Quello che succede è che hai creato la possibilità di un sovraccarico del buffer. E questa è una cosa negativa dappertutto.

Per rispondere ad alcune altre (ovvie) domande ovvie:

  1. Stai mescolando array in stile C con C ++. Non sono un guru del C ++, ma so che il C ++ ha modi migliori (più sicuri) di gestire gli array

  2. Se la classe ha già le variabili membro, perché è necessario passarle? Questa è più una questione architettonica.

Altre persone con più esperienza in C ++ (ho smesso di usarlo 10 o 15 anni fa) potrebbero avere modi più eloquenti di spiegare i problemi e probabilmente ne usciranno anche altri.


Anzi, vector<int>sarebbe più adatto; la lunghezza sarebbe quindi inutile. In alternativa const int len, poiché l'array a lunghezza variabile non fa parte dello standard C ++ (sebbene alcuni compilatori lo supportino, perché è una funzionalità opzionale in C).
Christophe,

5
@Christophe L'autore stava usando un array di dimensioni fisse, che dovrebbe essere sostituito con il std::arrayquale non si deteriora quando lo passa ai parametri, ha un modo più conveniente per ottenere le sue dimensioni, è copiabile e ha accesso con controllo dei limiti opzionale, senza costi generali Matrici in stile C.
Cubico

1
@Cubic: ogni volta che vedo codice che dichiara array con una dimensione fissa arbitraria (come 100), sono abbastanza sicuro che l'autore sia un principiante che non ha idea delle implicazioni di questa assurdità ed è una buona idea raccomandarlo cambiando questo design in qualcosa con una dimensione variabile.
Doc Brown,

Gli array vanno bene.
Lightness Races con Monica l'

... per coloro che non capivano cosa intendo, vedi anche Zero One Infinity Rule
Doc Brown,

48

Chiamare un metodo di classe con alcune variabili di classe non è necessariamente negativo. Ma farlo al di fuori della classe è una pessima idea e suggerisce un difetto fondamentale nella progettazione di OO, vale a dire l'assenza di un corretto incapsulamento :

  • Qualsiasi codice che usi la tua classe dovrebbe sapere che lenè la lunghezza dell'array e usarlo in modo coerente. Questo va contro il principio della minima conoscenza . Tale dipendenza dai dettagli interni della classe è estremamente soggetta a errori e rischiosa.
  • Ciò renderebbe molto difficile l'evoluzione della classe (ad esempio se un giorno vorresti cambiare gli array legacy e lenuno più moderno std::vector<int>), dal momento che richiederebbe anche di cambiare tutto il codice usando la tua classe.
  • Qualsiasi parte del codice potrebbe provocare il caos nei tuoi MyClassoggetti corrompendo alcune variabili pubbliche senza rispettare le regole (i cosiddetti invarianti di classe )
  • Infine, il metodo è in realtà indipendente dalla classe, poiché funziona solo con i parametri e non dipende da nessun altro elemento di classe. Questo tipo di metodo potrebbe benissimo essere una funzione indipendente al di fuori della classe. O almeno un metodo statico.

Dovresti refactificare il tuo codice e:

  • rendi variabili della tua classe privateo protected, a meno che non ci sia una buona ragione per non farlo.
  • progetta i tuoi metodi pubblici come azioni da svolgere sulla classe (ad es object.action(additional parameters for the action).:).
  • Se dopo questo refactoring avresti ancora alcuni metodi di classe che devono essere chiamati con variabili di classe, renderli protetti o privati ​​dopo aver verificato che sono funzioni di utilità che supportano i metodi pubblici.

7

Quando l'intenzione di questo progetto è che si desidera poter riutilizzare questo metodo per i dati che non provengono da questa istanza di classe, è possibile dichiararlo come staticmetodo .

Un metodo statico non appartiene a nessuna particolare istanza di una classe. È piuttosto un metodo della classe stessa. Poiché i metodi statici non vengono eseguiti nel contesto di una particolare istanza della classe, possono accedere solo alle variabili membro che sono anche dichiarate come statiche. Considerando che il tuo metodo addnon fa riferimento a nessuna delle variabili membro dichiarate in MyClass, è un buon candidato per essere dichiarato statico.

Tuttavia, i problemi di sicurezza menzionati dalle altre risposte sono validi: Non si sta verificando se entrambi gli array sono almeno lenlunghi. Se vuoi scrivere C ++ moderno e robusto, dovresti evitare di usare array. Usa la classe std::vectorinvece quando possibile. Contrariamente alle normali matrici, i vettori conoscono le proprie dimensioni. Quindi non è necessario tenere traccia delle loro dimensioni da soli. La maggior parte (non tutti!) Dei loro metodi esegue anche il controllo automatico dei limiti, il che garantisce che si ottiene un'eccezione quando si legge o si scrive oltre la fine. L'accesso regolare all'array non esegue il controllo associato, il che si traduce nel migliore dei casi in un segfault e potrebbe causare una vulnerabilità di sovraccarico del buffer sfruttabile nel peggiore dei casi.


1

Un metodo in una classe manipola idealmente i dati incapsulati all'interno della classe. Nel tuo esempio non c'è motivo per cui il metodo add abbia parametri, basta usare le proprietà della classe.


0

Passare (parte di) un oggetto a una funzione membro va bene. Quando si implementano le proprie funzioni, è sempre necessario essere consapevoli del possibile aliasing e delle relative conseguenze.

Ovviamente, il contratto potrebbe lasciare indefiniti alcuni tipi di alias anche se la lingua lo supporta.

Esempi di spicco:

  • Self-assegnazione. Questo dovrebbe essere un, forse costoso, no-op. Non pessimizzare il caso comune per questo.
    D'altro canto, l'assegnazione di un movimento autonomo, mentre non dovrebbe esplodere in faccia, non deve essere una no-op.
  • Inserimento di una copia di un elemento in un contenitore nel contenitore. Qualcosa del genere v.push_back(v[2]);.
  • std::copy() presuppone che la destinazione non inizi all'interno della fonte.

Ma il tuo esempio ha diversi problemi:

  • La funzione membro non utilizza nessuno dei suoi privilegi, né fa riferimento a this. Si comporta come una funzione di utilità gratuita che si trova semplicemente nel posto sbagliato.
  • La tua classe non cerca di presentare alcun tipo di astrazione . In linea con ciò, non cerca di incapsulare le sue viscere, né di mantenere alcun invariante . È una stupida borsa di elementi arbitrari.

In sintesi: Sì, questo esempio è negativo. Ma non perché la funzione membro ottiene oggetti membro passati.


0

In particolare, fare questa è una cattiva idea per diversi buoni motivi, ma non sono sicuro che siano tutti parte integrante della tua domanda:

  • Avere variabili di classe (variabili effettive, non costanti) è qualcosa da evitare, se possibile, e dovrebbe innescare una seria riflessione sulla necessità assoluta.
  • Lasciare l'accesso in scrittura a queste variabili di classe completamente aperto a tutti è quasi universalmente negativo.
  • Il tuo metodo di classe finisce per non essere correlato alla tua classe. Ciò che è realmente è una funzione che aggiunge i valori di un array ai valori di un altro array. Questo tipo di funzione dovrebbe essere una funzione in un linguaggio che ha funzioni e dovrebbe essere un metodo statico di una "classe falsa" (cioè una raccolta di funzioni senza dati o comportamento dell'oggetto) in linguaggi che non hanno funzioni.
  • In alternativa, rimuovi questi parametri e trasformalo in un metodo di classe reale, ma sarebbe comunque male per i motivi menzionati in precedenza.
  • Perché int array? vector<int>ti semplificherebbe la vita.
  • Se questo esempio è davvero rappresentativo del tuo progetto, considera di inserire i tuoi dati negli oggetti e non nelle classi e di implementare anche i costruttori per questi oggetti in modo da non dover modificare il valore dei dati degli oggetti dopo la creazione.

0

Questo è un classico approccio "C con classi" al C ++. In realtà, questo non è ciò che scriverebbe un programmatore C ++ esperto. Per uno, l'uso di array C grezzi è praticamente universalmente scoraggiato, a meno che non si stia implementando una libreria di contenitori.

Qualcosa del genere sarebbe più appropriato:

// Don't forget to compile with -std=c++17
#include <iostream>
using std::cout; // This style is less common, but I prefer it
using std::endl; // I'm sure it'll incite some strongly opinionated comments

#include <array>
using std::array;

#include <algorithm>

#include <vector>
using std::vector;


class MyClass {
public: // Begin lazy for brevity; don't do this
    std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
    std::array<int, 5> arr2 = {10, 10, 10, 10, 10};
};

void elementwise_add_assign(
    std::array<int, 5>& lhs,
    const std::array<int, 5>& rhs
) {
    std::transform(
        lhs.begin(), lhs.end(), rhs.begin(),
        lhs.begin(),
        [](auto& l, const auto& r) -> int {
            return l + r;
        });
}

int main() {
    MyClass foo{};

    elementwise_add_assign(foo.arr1, foo.arr2);

    for(auto const& value: foo.arr1) {
        cout << value << endl; // arr1 values have been added to
    }

    for(auto const& value: foo.arr2) {
        cout << value << endl; // arr2 values remain unaltered
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.