it-swarm.com.de

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

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

add kann 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 sollte oder nicht?

14
paper man

Es mag Dinge mit der Klasse geben, 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 Funktion add übergeben wird. Sicher hoffen Sie, dass es eines der Mitgliedsarrays ist, aber was passiert, wenn jemand ein anderes Array mit einer kleineren Größe als 100 oder eine Länge von mehr als 100 übergibt?

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

Um noch einige (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 hat, mit Arrays umzugehen

  2. Wenn die Klasse bereits über die Mitgliedsvariablen verfügt, warum müssen Sie diese ü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 eloquentere Möglichkeiten, die Probleme zu erklären, und werden wahrscheinlich auch mehr Probleme haben.

33
Peter M

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

  • Jeder Code, der Ihre Klasse verwendet, muss wissen, dass len die Länge des Arrays ist, und es konsistent verwenden. Dies widerspricht dem Prinzip des geringsten Wissens . Eine solche Abhängigkeit von den inneren Details der Klasse ist äußerst fehleranfällig und riskant.
  • Dies würde die Entwicklung der Klasse sehr schwierig machen (z. B. wenn Sie eines Tages die Legacy-Arrays und len in ein moderneres std::vector<int> Ändern möchten), da dies auch erforderlich wäre Ändern Sie den gesamten Code mit Ihrer Klasse.
  • Jeder Teil des Codes kann Ihre MyClass Objekte zerstören, indem einige öffentliche Variablen beschädigt werden, 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 sehr gut eine unabhängige Funktion außerhalb der Klasse sein. Oder zumindest eine statische Methode.

Sie sollten Ihren Code umgestalten und:

  • machen Sie Ihre Klassenvariablen private oder 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. B.: object.action(additional parameters for the action)).
  • 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.
48
Christophe

Wenn mit diesem Entwurf beabsichtigt wird, dass Sie diese Methode für Daten wiederverwenden möchten, die nicht aus dieser Klasseninstanz stammen, möchten Sie sie möglicherweise als static method 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 Mitgliedsvariablen zugreifen, die ebenfalls als statisch deklariert sind. Wenn man bedenkt, dass Ihre Methode add nicht auf eine der in MyClass deklarierten Mitgliedsvariablen verweist, ist sie ein guter Kandidat, um als statisch deklariert zu werden.

Die in den anderen Antworten genannten Sicherheitsprobleme sind jedoch gültig: Sie überprüfen nicht, ob beide Arrays mindestens len lang sind. Wenn Sie modernes und robustes C++ schreiben möchten, sollten Sie die Verwendung von Arrays vermeiden. Verwenden Sie die Klasse std::vector stattdessen wann immer möglich. Im Gegensatz zu normalen Arrays kennen Vektoren ihre eigene Größe. Sie müssen also nicht selbst die Größe verfolgen. Die meisten (nicht alle!) Methoden führen auch eine automatische gebundene Prü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 eine ausnutzbare Sicherheitsanfälligkeit durch Pufferüberlauf schlimmer verursachen kann.

7
Philipp

Eine Methode in einer Klasse manipuliert idealerweise gekapselte Daten innerhalb der Klasse. In Ihrem Beispiel gibt es keinen Grund für die add-Methode, Parameter zu haben. Verwenden Sie einfach die Eigenschaften der Klasse.

1
Rik D

Das Übergeben (eines Teils) eines Objekts an eine Mitgliedsfunktion ist in Ordnung. Bei der Implementierung Ihrer Funktionen müssen Sie immer sich des möglichen Aliasing bewusst sein und die Konsequenzen daraus.

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

Prominente Beispiele:

  • Selbstzuweisung. Dies sollte ein möglicherweise teurer No-Op sein. Pessimisieren Sie nicht den allgemeinen Fall dafür.
    Selbstbewegungszuweisung hingegen muss kein No-Op sein, obwohl sie nicht in Ihrem Gesicht explodieren sollte.
  • Einfügen einer Kopie eines Elements in einen Container in den Container. So etwas wie v.Push_back(v[2]);.
  • std::copy() geht davon aus, dass das Ziel nicht innerhalb der Quelle beginnt.

Ihr Beispiel hat jedoch andere Probleme:

  • Die Member-Funktion verwendet keine ihrer Privilegien und verweist auch nicht auf this. Es wirkt wie eine kostenlose Utility-Funktion, die sich einfach am falschen Ort befindet.
  • Ihre Klasse versucht nicht, irgendeine Art von Abstraktion zu präsentieren. In Übereinstimmung damit versucht es nicht, seine Innereien zu kapseln, noch irgendwelche Invarianten beizubehalten. Es ist eine blöde Tüte mit willkürlichen Elementen.

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

0
Deduplicator

Dies zu tun ist aus mehreren guten Gründen eine schlechte Idee, aber ich bin nicht sicher, ob alle für Ihre Frage von wesentlicher Bedeutung sind:

  • Klassenvariablen (tatsächliche Variablen, keine Konstanten) sollten nach Möglichkeit vermieden werden und sollten ernsthaft darüber nachdenken, ob Sie sie unbedingt benötigen.
  • Es ist fast überall schlecht, den Schreibzugriff auf diese Klassenvariablen für alle offen zu lassen.
  • 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 hinzufügt. Diese Art von Funktion sollte eine Funktion in einer Sprache sein, die Funktionen hat, und sollte eine statische Methode einer "gefälschten Klasse" (d. H. Eine Sammlung von Funktionen ohne Daten oder Objektverhalten) in Sprachen sein, die keine Funktionen haben.
  • Alternativ können Sie diese Parameter entfernen und in eine echte Klassenmethode umwandeln. Aus den oben genannten Gründen wäre dies jedoch immer noch schlecht.
  • Warum int Arrays? vector<int> würde dir das Leben leichter machen.
  • Wenn dieses Beispiel wirklich repräsentativ für Ihr Design ist, sollten 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.
0
Kafein

Dies ist ein klassischer "C mit Klassen" -Ansatz für C++. In Wirklichkeit ist dies nicht das, was ein erfahrener C++ - Programmierer schreiben würde. Zum einen wird von der Verwendung von rohen C-Arrays allgemein abgeraten, es sei denn, Sie implementieren eine Containerbibliothek.

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