Ist es eine schlechte Idee, eine Klassenmethode zu erstellen, an die Klassenvariablen übergeben werden?

14

Folgendes meine ich:

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);
}

addkann bereits auf alle benötigten Variablen zugreifen, da es sich um eine Klassenmethode handelt. Ist dies also eine schlechte Idee? Gibt es Gründe, warum ich das tun soll oder nicht?

Badeente
quelle
13
Wenn Sie Lust dazu haben, deutet dies auf ein schlechtes Design hin. Die Eigenschaften der Klasse gehören wahrscheinlich woanders hin.
Bitsoflogic
11
Snippet ist zu klein. Diese Ebene gemischter Abstraktionen tritt in Snippets dieser Größe nicht auf. Sie können dies auch mit einem guten Text zu Entwurfsüberlegungen beheben.
Joshua
16
Ich verstehe ehrlich gesagt nicht, warum Sie das überhaupt tun würden. Was hast du davon? Warum nicht einfach eine No-Arg- addMethode verwenden, die ihre Interna direkt verarbeitet? Warum nur?
Ian Kemp
2
@IanKemp Oder haben Sie eine addMethode, die Argumente akzeptiert , aber nicht als Teil einer Klasse existiert. Nur eine reine Funktion zum Addieren von zwei Arrays.
Kevin
Fügt die add-Methode immer arr1 und arr2 hinzu oder kann sie verwendet werden, um irgendetwas hinzuzufügen?
user253751

Antworten:

33

Es gibt vielleicht Dinge mit der Klasse, die ich anders machen würde, aber um die direkte Frage zu beantworten, wäre meine Antwort

Ja, das ist eine schlechte Idee

Mein Hauptgrund dafür ist, dass Sie keine Kontrolle darüber haben, was an die addFunktion übergeben wird. Sicher, Sie hoffen, dass es sich um eines der Member-Arrays handelt, aber was passiert, wenn jemand ein anderes Array mit einer Größe von weniger als 100 übergibt oder Sie eine Länge von mehr als 100 übergeben?

Was passiert ist, dass Sie die Möglichkeit eines Pufferüberlaufs geschaffen haben. Und das ist eine schlechte Sache.

Um einige weitere (für mich) offensichtliche Fragen zu beantworten:

  1. Sie mischen Arrays im C-Stil mit C ++. Ich bin kein C ++ - Guru, aber ich weiß, dass C ++ bessere (sicherere) Möglichkeiten für den Umgang mit Arrays bietet

  2. Wenn die Klasse bereits über die Mitgliedsvariablen verfügt, warum müssen Sie sie übergeben? Dies ist eher eine architektonische Frage.

Andere Leute mit mehr C ++ - Erfahrung (ich habe es vor 10 oder 15 Jahren nicht mehr verwendet) haben möglicherweise mehr beredte Möglichkeiten, die Probleme zu erklären, und werden wahrscheinlich auch mehr Probleme haben.

Peter M
quelle
Wäre in der Tat vector<int>besser geeignet; Länge wäre dann nutzlos. Alternativ const int len, da Array variabler Länge nicht Teil des C ++ - Standards ist (obwohl es einige Compiler unterstützen, da es eine optionale Funktion in C ist).
Christophe
5
@Christophe Author verwendete ein Array mit fester Größe, das durch ein Array mit fester Größe ersetzt werden sollte, das bei std::arrayder Übergabe an Parameter nicht abfällt , dessen Größe bequemer ermittelt werden kann, das kopierbar ist und auf das mit optionaler Begrenzungsprüfung zugegriffen werden kann, ohne dass ein Overhead entsteht Arrays im C-Stil.
Cubic
1
@Cubic: Wenn ich sehe, dass Code Arrays mit einer willkürlichen festen Größe (wie 100) deklariert, bin ich mir ziemlich sicher, dass der Autor ein Anfänger ist, der keine Ahnung von den Auswirkungen dieses Unsinns hat, und es ist eine gute Idee, ihn / sie zu empfehlen Ändern dieses Entwurfs zu etwas mit einer variablen Größe.
Doc Brown
Die Arrays sind in Ordnung.
Leichtigkeit Rennen mit Monica
... für diejenigen, die nicht verstanden haben, was ich meine, siehe auch Zero One Infinity Rule
Doc Brown
48

Das Aufrufen einer Klassenmethode mit einigen Klassenvariablen ist nicht unbedingt schlecht. Dies von außerhalb der Klasse zu tun, ist eine sehr schlechte Idee und weist auf einen grundlegenden Fehler in Ihrem OO-Design hin, nämlich das Fehlen einer ordnungsgemäßen Kapselung :

  • Jeder Code, der Ihre Klasse verwendet, muss lendie Länge des Arrays kennen und diese konsistent verwenden. Dies widerspricht dem Grundsatz des geringsten Wissens . Eine solche Abhängigkeit von den inneren Details der Klasse ist extrem fehleranfällig und riskant.
  • Dies würde die Entwicklung der Klasse sehr erschweren (z. B. wenn Sie eines Tages die alten Arrays und lenmodernere Arrays ändern std::vector<int>möchten), da Sie auch den gesamten Code mithilfe Ihrer Klasse ändern müssten.
  • Jeder Teil des Codes kann Verwüstungen in Ihren MyClassObjekten anrichten, indem er einige öffentliche Variablen beschädigt , ohne die Regeln zu beachten (sogenannte Klasseninvarianten ).
  • Schließlich ist die Methode in Wirklichkeit unabhängig von der Klasse, da sie nur mit den Parametern arbeitet und von keinem anderen Klassenelement abhängt. Diese Art von Methode könnte durchaus eine unabhängige Funktion außerhalb der Klasse sein. Oder zumindest eine statische Methode.

Sie sollten Ihren Code überarbeiten und:

  • Machen Sie Ihre Klassenvariablen privateoder protected, es sei denn, es gibt einen guten Grund, dies nicht zu tun.
  • Entwerfen Sie Ihre öffentlichen Methoden als Aktionen, die für die Klasse ausgeführt werden sollen (z object.action(additional parameters for the action). B.:) .
  • Wenn Sie nach diesem Refactoring noch einige Klassenmethoden haben, die mit Klassenvariablen aufgerufen werden müssen, machen Sie sie geschützt oder privat, nachdem Sie überprüft haben, dass es sich um Dienstprogrammfunktionen handelt, die die öffentlichen Methoden unterstützen.
Christophe
quelle
7

Wenn Sie diese Methode für Daten wiederverwenden möchten, die nicht aus dieser Klasseninstanz stammen, möchten Sie sie möglicherweise als staticMethode deklarieren .

Eine statische Methode gehört zu keiner bestimmten Instanz einer Klasse. Es ist eher eine Methode der Klasse selbst. Da statische Methoden nicht im Kontext einer bestimmten Instanz der Klasse ausgeführt werden, können sie nur auf Membervariablen zugreifen, die auch als statisch deklariert sind. In Anbetracht dessen, dass Ihre Methode addauf keine der in deklarierten Membervariablen verweist MyClass, ist sie ein guter Kandidat für die Deklaration als statisch.

Die in den anderen Antworten genannten Sicherheitsprobleme sind jedoch gültig: Sie überprüfen nicht, ob beide Arrays mindestens lenlang sind. Wenn Sie modernes und robustes C ++ schreiben möchten, sollten Sie Arrays vermeiden. Verwenden Sie std::vectorstattdessen nach Möglichkeit die Klasse . Im Gegensatz zu regulären Arrays kennen Vektoren ihre eigene Größe. Sie müssen sich also nicht selbst um ihre Größe kümmern. Die meisten (nicht alle!) Ihrer Methoden führen auch eine automatische Bindungsprüfung durch, die garantiert, dass Sie eine Ausnahme erhalten, wenn Sie nach dem Ende lesen oder schreiben. Regelmäßiger Array-Zugriff führt keine gebundene Überprüfung durch, was bestenfalls zu einem Segfault führt und im schlimmsten Fall zu einer ausnutzbaren Sicherheitsanfälligkeit durch Pufferüberlauf führen kann .

Philipp
quelle
1

Eine Methode in einer Klasse manipuliert gekapselte Daten innerhalb der Klasse auf ideale Weise. In Ihrem Beispiel gibt es keinen Grund, warum die add-Methode Parameter hat. Verwenden Sie einfach die Eigenschaften der Klasse.

Rik D
quelle
0

Das Übergeben (eines Teils davon) eines Objekts an eine Member-Funktion ist in Ordnung. Bei der Implementierung Ihrer Funktionen müssen Sie immer das mögliche Aliasing und die daraus resultierenden Konsequenzen berücksichtigen.

Natürlich kann der Vertrag einige Arten von Aliasing unbestimmt lassen, auch wenn die Sprache dies unterstützt.

Prominente Beispiele:

  • Selbstzuweisung. Dies sollte ein, möglicherweise teurer, No-Op sein. Pessimieren Sie nicht den üblichen Fall dafür.
    Self-Move-Assignment hingegen muss kein No-Op sein, auch wenn es nicht in deinem Gesicht explodieren sollte.
  • Einfügen einer Kopie eines Elements in einem Container in den Container. So etwas wie v.push_back(v[2]);.
  • std::copy() geht davon aus, dass das Ziel nicht in der Quelle beginnt.

Aber Ihr Beispiel hat verschiedene Probleme:

  • Die member-Funktion verwendet keine ihrer Berechtigungen und verweist auch nicht auf this. Es verhält sich wie eine kostenlose Utility-Funktion, die sich einfach am falschen Ort befindet.
  • Ihre Klasse versucht nicht, irgendeine Art von Abstraktion zu präsentieren . Dementsprechend versucht es nicht, seine Innereien einzukapseln und keine Invarianten beizubehalten . Es ist eine dumme Tasche von willkürlichen Elementen.

Fazit: Ja, dieses Beispiel ist schlecht. Aber nicht, weil die Member-Funktion Member-Objekte übergeben bekommt.

Deduplizierer
quelle
0

Insbesondere ist dies aus mehreren guten Gründen eine schlechte Idee, aber ich bin mir nicht sicher, ob all diese Gründe für Ihre Frage von Bedeutung sind:

  • Klassenvariablen (tatsächliche Variablen, keine Konstanten) sollten nach Möglichkeit vermieden und ernsthaft darüber nachgedacht werden, ob sie unbedingt benötigt werden.
  • Schreibzugriff auf diese Klassenvariablen für jedermann offen zu lassen, ist fast generell schlecht.
  • Ihre Klassenmethode hat letztendlich nichts mit Ihrer Klasse zu tun. Was es wirklich ist, ist eine Funktion, die die Werte eines Arrays zu den Werten eines anderen Arrays addiert. Diese Art von Funktion sollte eine Funktion in einer Sprache sein, die Funktionen hat, und sollte eine statische Methode einer "gefälschten Klasse" (dh eine Sammlung von Funktionen ohne Daten- oder Objektverhalten) in Sprachen sein, die keine Funktionen haben.
  • Entfernen Sie alternativ diese Parameter, und wandeln Sie sie in eine echte Klassenmethode um. Aus den oben genannten Gründen ist sie jedoch weiterhin fehlerhaft.
  • Warum int-Arrays? vector<int>würde dein Leben leichter machen.
  • Wenn dieses Beispiel wirklich repräsentativ für Ihren Entwurf ist, können Sie Ihre Daten in Objekte und nicht in Klassen einfügen und Konstruktoren für diese Objekte so implementieren, dass der Wert der Objektdaten nach der Erstellung nicht geändert werden muss.
Kafein
quelle
0

Dies ist ein klassischer "C mit Klassen" -Ansatz für C ++. In Wirklichkeit würde dies kein erfahrener C ++ - Programmierer schreiben. Zum einen wird von der Verwendung von Raw-C-Arrays generell abgeraten, es sei denn, Sie implementieren eine Container-Bibliothek.

So etwas wäre angemessener:

// 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
    }
}
Alexander - Setzen Sie Monica wieder ein
quelle