Struct oder class - Wann was bzw. Vorteile?

Eine Frage dazu :slight_smile:
Warum gehst Du da über


pin = aPin;

und verwendest nicht aPin?
:face_with_raised_eyebrow:

Sollte die Frage an mich gehen, so habe ich den Programmschnipsel einfach so übernommen und nur die Reihenfolge getauscht.

Die Methode setze nutzt die Variable pin, die in der Methode init gefüllt wird:

struct relaisTyp {
  byte pin;
  byte zustand;
  void init(byte aPin) {
    pin = aPin;
    digitalWrite(pin, LOW);
    pinMode(pin, OUTPUT);
  }
  void setze(byte level) {
    zustand = level;
    digitalWrite(pin, zustand);
    Serial.print(zustand == HIGH ? "HIGH " : "LOW  ");
  }
};
...
void relaisInitialisieren() {
  // Hier werden die relais-Objekte initialisiert, indem ihnen der zugehörige Pin
  // zugewiesen wird.
  for (int i = 0; i < relaisAnzahl; i++) {
    relais[i].init(relaisPin[i]);
    relais[i].setze(LOW);  // Alle Leuchten werden hier mal auf LOW gesetzt.
  }
}

Ob das nun besonders pfiffig, überflüssig oder ein Fehler ist, wäre zu prüfen.

1 Like

Ich danke dir mal wieder :slight_smile:

Serial.print(zustand ? "HIGH " : "LOW ");
wäre etwas besser, da zustand ein byte ist, bei dem nur 0 als LOW oder false wirkt, alle 254 anderen aber bei digitalWrite wie HIGH wirken.
Die interne Umwandlung von byte nach bool macht es richtig, zustand == HIGH macht etwas anderes, nämlich einen numerischen Vergleich.

Die Frage geht wohl an mich ... :wink:

In der fraglichen Ausführung ist die Variable aPin nur in der Funktion init() verfügbar. Bei der Funktion setze() brauchen wir die Pin-Nummer jedoch auch. Zwangsläufig kann der Name des Parameters bei init() dabei nicht identisch mit dem der Struktur-internen Variablen sein.

Dieses Vorgehen wird bei Klassen dazu genutzt, den direkten Zugriff auf interne Variable zu verhindern. Das geht dort über dafür zu schreibende Funktionen, bei denen diese ggf. Wertebereiche überprüfen und/oder andere wesentliche Aufgaben wahrnehmen. Relais[1].init(5); ruft z.B. noch pinMode() auf, was bei Relais[1].pin = 5; nicht automatisch erfolgt.

Um den Zugriff zu verhindern, kann man in der Struktur die internen Anteile als private, die offenen als public deklarieren.

Ich hatte dies nur vorbereitet, nun hier einmal beispielhaft für relaisTyp umgesetzt. Den berechtigten Hinweis von @michael_x habe ich ebenfalls berücksichtigt (die strukturinterne Variable "zustand" kann nun nur entweder den Wert LOW oder den Wert HIGH einnehmen, egal welchen anderen Wert man bei setze() übergibt).

struct relaisTyp {
public:
  void init(byte aPin) {
    pin = aPin;
    digitalWrite(pin, LOW);
    pinMode(pin, OUTPUT);
  }
  void setze(byte level) {
    zustand = level == 0 ? LOW : HIGH;
    digitalWrite(pin, zustand);
    Serial.print(zustand == HIGH ? "HIGH " : "LOW  ");
  }
private:
  byte pin;
  byte zustand;
};

Wenn man im Programm den Wert von pin oder zustand später nochmals auslesen können möchte, würde man zusätzliche Funktionen dafür implementieren:

struct relaisTyp {
public:
  byte getPin(){
    return pin;
  }
  byte getZustand(){
    return zustand;
  }
// ... hier folgt die weitere Definition der Struktur wie oben

Mit relais[0].getPin() erhält man dann den entsprechenden Wert, ohne die interne Variable verändern zu können.

Statt "struct" kann man im o.a. Beispiel auch "class" schreiben ... schon haben wir eine Klasse relaisTyp.

Ich hoffe, das erläutert den Hintergrund ausreichend ...

1 Like

Danke dir für die Erklärung :slight_smile:

Ich hätte da jetzt in der Tat eine class erwartet….
Ich habe struct bis jetzt nur zum „Bündeln“ von Daten genutzt.

Wenn ich es korrekt verstanden habe, ist der Unterschied struct/class default Sichtbarkeit public/private.

Wie weiß man denn, wann man was am besten nimmt?
Wann class? Wann besser struct?
Wenn beide das selbe können?

Egal. Eigentlich.

struct gab es schon seit den Anfängen von C.
Objektorientierte Programmierung geht mit class.
Mein Tip: sobald Methoden dazukommen, oder du nicht alles public haben willst, nenne es class.

2 Likes

Struct ist eigentlich dasselbe wie class. Nur ist bei struct standardmäßig alles public, bei class jedoch private.

Man kann auch mit struct objektorientiert programmieren.

1 Like

Gerne.

Um den Thread nicht komplett zu hijacken, noch ein letzter Post zur Sache:

Off (original) Topic

Mit struct zu beginnen, hat i.d.R. den psychologischen Vorteil, dass es für jemanden, der sich mit Klassen noch nicht auskennt, erst einmal weniger abschreckend klingt ... :wink: Und wenn man dann erkannt hat, dass eine Struktur auch Funktionen beinhalten kann, erscheint der Weg zur Klasse nicht mehr so schwierig. Tatsächlich unterscheiden sich beide nur marginal (wie oben von @michael_x und @Kai-R schon erläutert).

struct-Funktionen können - wie bei class - innerhalb der Definition wie auch extern erfolgen, dann werden sie durch den struct/class Namen referenziert. Innerhalb der struct/class Definition müssen sie nur mit ihrem Aufruf aufgeführt sein:

struct relaisTyp {
public:
  byte getPin() {
    return pin;
  }
  byte getZustand() {
    return zustand;
  }
  void init(byte aPin);
  void setze(byte level);
private:
  byte pin;
  byte zustand;
};

void relaisTyp::init(byte aPin) {
  pin = aPin;
  digitalWrite(pin, LOW);
  pinMode(pin, OUTPUT);
};

void relaisTyp::setze(byte level) {
  zustand = level == 0 ? LOW : HIGH;
  digitalWrite(pin, zustand);
  Serial.print(zustand == HIGH ? "HIGH " : "LOW  ");
};

Eine Besonderheit ist der Constructor, der ohne Datentyp aufgeführt wird und von vielen Klassen dazu benutzt wird, bestimmte Parameter mit der Deklaration zu übergeben. Dort muss man aber aufpassen, ob z.B. anzusprechende Hardware schon verfügbar ist. Es ist dann besser, bestimmte Funktionen erst im Setup() mit z.B. einer Funktion wie init() aufzurufen.

1 Like

@uwefed
Ich würde das gern noch etwas vertiefen, könntest Du das ab #86 abspalten bitte?

Hm.. Uwe ist doch ein Mod oder liege ich da falsch? :thinking:

Wenn er verhindert ist, kann ein anderer @Mods es abspalten?

Du liegst richtig, aber er lebt in Italien, genießt das Leben und heute am Valentinstag möglicherweise auch den Traum seines Herzens :smiling_face_with_three_hearts:

Tue es ihm gleich, entspanne und gib ihm Zeit.

Danke für Blumen
Aber
Den ganzen Tag keine Zeit gehabt ins Forum zu schauen;
bis 20 h gearbeitet und dann mal was essen gegangen. Dann mit Udo geplaudert und heimgefahren. Jetzt zuhause.
Es gibt nur mich als deutschsprechenden bzw gut deutschverstehenden Moderator.
Einen englischsprachigen, der mittels automatischen Übersetzer moderierenden Moderator möchte ich nicht (mehr) haben.
@ progger hab Probleme einen geeigneten Titel zu finden. würdest Du mir einen vorschlagen? Danke
Grüße Uwe

1 Like

Ja, da gab es schon Fehlentscheidungen, weil es diese inhaltlich nicht verstanden haben. So wurde ich da schon mal als der Beleidigte gesperrt, weil der fremdsprachige Mod die Übersetzung inhaltlich nicht verstanden hatte.

Gruß Tommy

Das ist immer eine gute Wahl :slight_smile:

Ja gern:

struct oder class - Wann was bzw. Vorteile?

Danke für die Arbeit! :+1:t2:

Wenn ich das richtig sehe, ist dies offiziell dein Thread @progger. Den Titel kannst du also selber anpassen. Bis @UweFederer aufgewacht ist.

Und ihn meiner Meinung nach auch gleich zumachen ( eine Antwort, -- z.B. @Kai-R #8 -- als Lösung markieren), da das wesentliche schon gesagt wurde. Aber wenn du es noch vertiefen willst, nur zu.

1 Like

Korrekt, wie in @Kai-R s Post beantwortet, ist es technisch grundsätzlich gleichwertig, welche der beiden Formen man nimmt.

Die Verwendung von class für Strukturen, in denen Funktionen und insbesondere Konstruktoren eingesetzt werden, ist allerdings die m.E. verbreitetste Anwendung.

Wie oben beschrieben, scheint mir struct für jemanden, der sich noch nicht mit dem Erstellen einer Klasse auseinandergesetzt hat, weniger "abschreckend". Das Erstaunen ist i.d.R. groß, wie klein dann der Schritt von struct zu class ist.

@progger, wenn Du allerdings noch weitere Fragen zum Thema class/struct hast, dann gerne posten!

Ansonsten ist es immer empfehlenswert, sich bei Interesse an C++ ein Fachbuch zuzulegen.

1 Like

Macht aber nur wirklich Sinn, wenn man den Pin zur Laufzeit ändern will.
Dann wäre eine Setter Methode z.B. setPin() evtl. sinniger

Naja.....

void init(byte pin) {
    this->pin = pin;
    digitalWrite(pin, LOW);
    pinMode(pin, OUTPUT);
  }

Alternativvorschlag:

class Relais // invertierend
{
  public:
    Relais(byte pin):pin{pin},zustand{false} {}
    void init()
    {
      digitalWrite(pin, HIGH);
      pinMode(pin, OUTPUT);
    }
    void set(bool value)
    {
      zustand = value;
      digitalWrite(pin,!zustand);
    }
    operator bool() const {return zustand;}
    
  private:
    const byte pin;
    bool zustand;
};

Relais relais {9};

void setup()
{
  Serial.begin(9600);
  relais.init();
}

void loop()
{
  relais.set(1);
  Serial.println(relais ? "Angezogen " : "Abgefallen  ");
  delay(1000);
  
  relais.set(0);
  Serial.println(relais ? "Angezogen " : "Abgefallen  ");
  delay(1000);
}

Oder gar so (was dann eher meinem Stil entspricht):

class Relais : public Printable // invertierend
{
  public:
    Relais(byte pin):pin{pin},zustand{false} {}
    void init()
    {
      digitalWrite(pin, HIGH);
      pinMode(pin, OUTPUT);
    }
    bool operator =(bool value)
    {
      zustand = value;
      digitalWrite(pin,!zustand);
      return *this;
    }
    operator bool() const {return zustand;}
    virtual size_t printTo(Print &p) const
    {
      return p.println(*this ? "Angezogen " : "Abgefallen  ");
    }
    
  private:
    const byte pin;
    bool zustand;
};

Relais relais {13};

void setup()
{
  Serial.begin(9600);
  relais.init();
}

void loop()
{
  relais = 1;
  Serial.println(relais);
  delay(1000);
  
  relais = 0;
  Serial.println(relais);
  delay(1000);
}

Wie auch immer, C++ bietet uns einige Möglichkeiten, wie man die Geschichte ausformt.
Ein Hoch auf die Vielfalt.

3 Likes

Ich Danke euch für eure Beispiele und Erklärungen.
Ich hatte gehofft, dass es doch klarere Antworten zu meinen Fragen geben würde :joy:

Oder ob es Vorteile des einen gibt.
Oder Nachteile des anderen.

Dann werde ich vorläufig
weiter Datenbündeln mit struct und wenn es darüber hinausgeht, werde ich eine Klasse probieren :slight_smile:

Es sein denn, jemand hat noch gute Hinweise dazu :slight_smile:

1 Like

In C++ ist der einzige Unterschied, dass bei struct die default-Sichtbarkeit public ist, bei class private. Früher (TM) in C var struct nur als Zusammenfassung von Daten möglich.

Es hat sich irgendwie eingebürgert, dass man für Daten struct benutzt, sonst class. Das ist aber nicht notwendig. Der "Vorteil" bei class ist die erzwungene Kapselung, d.h. man muss gezielt Sachen public setzen. Manche setzen bei class dann auch gleich public: oben hin, aber man kann halt mit allem (und in jeder Sprache) Sch... programmieren.

Gruß Tommy

1 Like