Klasseninstanz in anderer Klasse: Wie wird diese Instanz erzeugt.

Hallo Leute,

ich hoffe ich lerne heute wieder viel über den Arduino.

Ich möchte in einer Klasse eine Instanz (lcd) einer anderen Klasse (LiquidCrystal_I2C) als Attribut haben.

Da das ganze im Konstruktor initialisiert werden soll wird es ein Zeiger auf LiquidCrystal_I2C

Also

class HRMenu
{
  public:
    HRMenu();
  private: 
    LiquidCrystal_I2C*    lcd;  
  protected:
 };

Nun möchte ich die Instanz im Konstruktor erzeugen , aber so geht es nicht :

HRMenu::HRMenu()
{
  lcd = LiquidCrystal_I2C(0x27,20,4);
};

Als globale Variable würde ich mir die Instanz so erzeugen:

LiquidCrystal_I2C         lcd = (0x27,20,4);

Ich stehe auf dem Schlauch ...
Grüßle
Harry

HRMenu::HRMenu()
{
  lcd = new LiquidCrystal_I2C(0x27,20,4);
};

Sollte funktionieren...

Ist aber problematisch!

  1. Es ist eine versteckte Abhängigkeit
  2. Die dynamische Speicherverwaltung... da sollte man bei den AVRs drauf verzichten.

Besser:
Nutze "Constructor injection" wenn das Objekt unbedingt benötigt wird.
Nutze "Setter injection" wenn das Objekt optional ist.

Das OOP Pattern nennt sich dann "Dependency Injection Design Pattern"

Deutscher:
Übergib dem Konstruktor eine Instanz.

Beispiel:

class Test
{
  private:
  Stream *ser;

  public:
  Test(Stream* ser): ser(ser){}
  
  void tuWas()
  {
    ser->println(F("bin da"));  
  }
};

Test testinstanz(&Serial);

void setup() 
{
  Serial.begin(9600);
  testinstanz.tuWas();
}

void loop() {}

Das im Konstruktor übergeben ist hier korrekt

Wenn du doch new machen solltest, den Destruktor nicht vergessen!

Hmm,

ich bin etwas unzufrieden :confused:

Bei der Konstruktor-Übergabemethode brauche ich bereits vorher eine Instanz,
genau das wollte ich nicht.

Von new wird abgeraten ...

Hier mal meine Intention:

Ich möchte im Sinne des guten alten Information Hiding die Instanz schön im private-Teil verstecken
und dann nur noch via Methodenaufrufe darauf zugreifen. Im Grunde ist die Oberklasse ein Wrapper,
der verbirgt, dass und wie ich ein LCD verwende ...

Gibt es noch weitere Ideen ?

Grüßle

Harry

Es geht nicht anders. Alles was du im Konstruktor selbst ohne new erzeugen kannst existiert nur lokal dort.

Moment... Sollte doch anders gehen. Und zwar über die Initialisierungsliste des Konstruktors!

Ich möchte im Sinne des guten alten Information Hiding die Instanz schön im private-Teil verstecken
und dann nur noch via Methodenaufrufe darauf zugreifen. Im Grunde ist die Oberklasse ein Wrapper,
der verbirgt, dass und wie ich ein LCD verwende ...

Fehlschluss!

LiquidCrystal_I2C(0x27,20,4);
Verwendet Daten!
Diese Daten hart in eine Klasse zu kodieren ist ein Fehler.
Was passiert, wenn du ein anderes LCD verwenden willst?
Eine andere I2C Adresse, oder gar ein SPI LCD?
Die Klasse editieren?
Nein, nein!

Abhängigkeiten haben sichtbar zu sein.
Wartbar.
Sonst suchst du dir irgendwann einen Wolf.
Sonst bekommst du die Klasse nicht universell.

Tipp, befrage Google zu "Dependency Injection Design Pattern"
Das ist fast 30 Jahre alt!
Die Jungs/Mädels sind alle schon in die Falle getappt, welche gerade vor deiner Nase steht.

Also das hier kompiliert (für ein normales LCD, nicht I2C, aber egal):

#include "LiquidCrystal.h"

class LCDWrapper
{
public:
  LCDWrapper(byte rs, byte en, byte d0, byte d1, byte d2, byte d3) : lcd(rs, en, d0, d1, d2, d3)
  {
  }

  void begin(byte cols, byte rows)
  {
    lcd.begin(cols, rows);
  }
private:
  LiquidCrystal lcd;
};

LCDWrapper lcd(2, 3, 4, 5, 6, 7);

void setup()
{
  lcd.begin(16, 2);
}

void loop()
{
}

Und die Daten sind nach außen änderbar. Man muss aber einiges an Methoden durch den Wrapper schleifen

Was passiert, wenn du ein anderes LCD verwenden willst?

Das ist so oder so ein Problem, da die LCD Klassen generell nicht zueinander kompatibel sind und oft andere Methoden haben. Auch wenn man einen Zeiger auf die LCD Instanz übergibt, kann man dann nur genau diese LCD Klasse verwenden. I2C und parallel LCD wären wohl noch möglich wenn man die Initialisierung auch außerhalb der Klasse erledigt.
Das wäre nur wirklich universell wenn es eine gemeinsame abstrakte Oberklasse gäbe.

Danke für die Hilfe, ich gehe gleich ins stille Kämmerlein und suche die für mich beste Lösung aus :slight_smile:

Viele Grüße

Harry

Das ist so oder so ein Problem, da die LCD Klassen generell nicht zueinander kompatibel sind und oft andere Methoden haben. Auch wenn man einen Zeiger auf die LCD Instanz übergibt, kann man dann nur genau diese LCD Klasse verwenden. I2C und parallel LCD wären wohl noch möglich wenn man die Initialisierung auch außerhalb der Klasse erledigt.

Siehst du!
Genau das meine ich.
Damit wären wir bei dem berühmten "Adapter Design Pattern"
Welches allerdings nicht wirklich anwendbar ist, wenn die Objekte "hart" ineinander stecken.