CPP Problem ... ich verzweifel noch
Moderatoren: Heaterman, Finger, Sven, TDI, Marsupilami72, duese
CPP Problem ... ich verzweifel noch
Hallo Gemeinde,
evtl ist es hier offtopic, aber allgemein ist es ja
Ich spiel gerade mit dem Arduino und versuche mich da in OOP. In ABAP oder Java klappt das ja einigermaßen, aber hier habe ich gerade ein Problem.
ich habe eine klasse, die das Adafruit Motor Shield (AFS) verwenden soll. Dummerweise brauchen die Konstruktoren in der Klasse immer irgendwelche Daten, z.B. die Motornummer.
Meine Klasse:
class Fahrzeug (byte motorNummer){
private: byte motorNummer;
/// (1) Hier soll jetzt auch noch das Objekt für das AMS hin
public: Fahrzeug(byte Nr){ // Kontruktor
motorNummer = Nr;
// (2) Hier müsste jetzt das Objekt für das AMS mit der Motornummer irgendwie erzeugt werden.
}
}
In Spaghetticode erzeugt man den Motor so:
AF_DCMotor ersterMotor(motorNummer, MOTOR12_64KHZ);
Bei (1) kann ich das aber nicht machen, da ja die Motornummer da noch gar nicht bekannt ist.
Versuche in 1 sowas zu machen: AF_DCMotor ersterMotor; und in (2) dann ersterMotor(1, MOTOR12_64KHZ) funktionieren nicht... weil bei (1) ja der Konstruktor von AF_DCMotor aufgerufen wird und dem dann die Parameter fehlen.
Hat da jeman dein Tipp?
evtl ist es hier offtopic, aber allgemein ist es ja
Ich spiel gerade mit dem Arduino und versuche mich da in OOP. In ABAP oder Java klappt das ja einigermaßen, aber hier habe ich gerade ein Problem.
ich habe eine klasse, die das Adafruit Motor Shield (AFS) verwenden soll. Dummerweise brauchen die Konstruktoren in der Klasse immer irgendwelche Daten, z.B. die Motornummer.
Meine Klasse:
class Fahrzeug (byte motorNummer){
private: byte motorNummer;
/// (1) Hier soll jetzt auch noch das Objekt für das AMS hin
public: Fahrzeug(byte Nr){ // Kontruktor
motorNummer = Nr;
// (2) Hier müsste jetzt das Objekt für das AMS mit der Motornummer irgendwie erzeugt werden.
}
}
In Spaghetticode erzeugt man den Motor so:
AF_DCMotor ersterMotor(motorNummer, MOTOR12_64KHZ);
Bei (1) kann ich das aber nicht machen, da ja die Motornummer da noch gar nicht bekannt ist.
Versuche in 1 sowas zu machen: AF_DCMotor ersterMotor; und in (2) dann ersterMotor(1, MOTOR12_64KHZ) funktionieren nicht... weil bei (1) ja der Konstruktor von AF_DCMotor aufgerufen wird und dem dann die Parameter fehlen.
Hat da jeman dein Tipp?
Re: CPP Problem ... ich verzweifel noch
Macht man sowas in C++ nicht mit templates ?
Re: CPP Problem ... ich verzweifel noch
In lesbar
Eine Lösung ist bei (1) einen smart pointer anzulegen und dann im Konstruktor das Objekt zu erzeugen.
z.B.:
Code: Alles auswählen
class Fahrzeug (byte motorNummer){
private:
byte motorNummer;
/// (1) Hier soll jetzt auch noch das Objekt für das AMS hin
public:
Fahrzeug(byte Nr){ // Kontruktor
motorNummer = Nr;
// (2) Hier müsste jetzt das Objekt für das AMS mit der Motornummer irgendwie erzeugt werden.
}
}
z.B.:
Code: Alles auswählen
class Fahrzeug{
private:
byte motorNummer;
shared_ptr<AF_DCMotor> ersterMotor;
public:
Fahrzeug(byte Nr){ // Kontruktor
motorNummer = Nr;
ersterMotor = make_shared<AF_DCMotor>(motorNummer, MOTOR12_64KHZ);
}
}
Re: CPP Problem ... ich verzweifel noch
Hallo Lukas,
erst mal danke für die lesbare Formatierung
leider gibt die Zeile:
schon alleine beim complieren die Fehlermeldung "shared_ptr is not a template"
Und ich google mir schon seit gestern den Wolf, finde aber keine einigermaßen verständliche Lösung für mein Problem. Das mit den Templates scheint in die richtige Richtung zu gehen. Aber ich glaube, ich weiß jetzt warum CPP aussterben wird
Ich habs dann noch mit Vererbung probiert in der ArtDas scheitert aber schon daran, dass das verf"$%"$§ CPP hier schon beim erzugen der Klasse My_DCMotor auch den Konstruktor von AF_DCMotor ausführen möchte...
erst mal danke für die lesbare Formatierung
leider gibt die Zeile:
Code: Alles auswählen
shared_ptr<AF_DCMotor> ersterMotor;
Und ich google mir schon seit gestern den Wolf, finde aber keine einigermaßen verständliche Lösung für mein Problem. Das mit den Templates scheint in die richtige Richtung zu gehen. Aber ich glaube, ich weiß jetzt warum CPP aussterben wird
Ich habs dann noch mit Vererbung probiert in der Art
Code: Alles auswählen
class My_DCMotor : public AF_DCMotor {
public: My_DCMotor() { // DefaultKonstruktor
// tu nichts
}
public: My_DCMotor( int nr, int modus) {
AF_DCMotor(nr, modus); // Aufruf des Vater-Konstruktors
}
};
Re: CPP Problem ... ich verzweifel noch
Ich glaube, du hast zu viel Java o.Ä. benutzt...
Instanzierung eines Objekts im Stile
macht man generell nicht.
Dem Konstruktor könnte man zwar per
Parameter mitgeben (so dass dann auch der richtige aufgerufen wird), aber man macht das Trotzdem nicht, weil das Objekt dann auf dem Stack landet (und dort ist der Platz knapp).
Man verwendet Pointer, dann sieht das fast nach Java aus (und funktioniert auch genauso):
Wenn man damit fertig ist, muss man das Ganze dann per delete entsorgen.
Was du da mit Templates willst, ist mir noch nicht ganz klar.
Instanzierung eines Objekts im Stile
Code: Alles auswählen
Typ Instanz;
Dem Konstruktor könnte man zwar per
Code: Alles auswählen
Typ Instanz(Parameter,Parameter,noch mehr Parameter);
Man verwendet Pointer, dann sieht das fast nach Java aus (und funktioniert auch genauso):
Code: Alles auswählen
Typ *Pointer;
Pointer = new Typ(Parameter...);
Was du da mit Templates willst, ist mir noch nicht ganz klar.
Re: CPP Problem ... ich verzweifel noch
Hi ferdim,
Aber der Tip mit den Pointern wars. Dieses Beispiel funktioniert:
Danke euch allen nochmal fürs Augen öffnen.
Und Java... naja ehrlich gesagt programmierte ich früher in bash und PHP und seit 20 Jahren verdiene ich meine Brötchen mit ABAP.
C hatte ich mal 2 Wochen 1998 gelernt und seither nichts mehr gemacht und C++ .... well... eigentlich noch gar nichts.
Am schlimmsten find ich eigentlich, dass die ganzen Bücher über OOP (egal ob C++ oder Java) von irgendwelchen Theoretikern geschrieben werden (ja ich hab auch eins und da kräftig nachgelesen, deine Lösung stand nicht drin) und praktische Beispiele oder gar Tipps fürs tägliche Leben da total untergehen
Prima, mir war das nämlich auch nicht klarferdimh hat geschrieben:Was du da mit Templates willst, ist mir noch nicht ganz klar.
Aber der Tip mit den Pointern wars. Dieses Beispiel funktioniert:
Code: Alles auswählen
class Reifen {
private:
String hersteller;
String profil;
public:
Reifen(String herst, String prof){ // constructor
hersteller = herst;
profil = prof;
}
void bericht() {
Serial.print("Hersteller: ");
Serial.print(hersteller);
Serial.print(" Profil: ");
Serial.println(profil);
}
};
class Auto {
private:
// hier sollen jetzt die Reifen (=Objekte) rein für rv, lv, rh, rl
Reifen *rv;
Reifen *lv;
Reifen *rh;
Reifen *rl;
byte dummy;
public:
void initalisiere_reifen(){
rv = new Reifen("Bridgestone", "gut");
lv = new Reifen("Dunlop", "gut");
rh = new Reifen("Fulda", "schlecht");
rl = new Reifen("Yokohama", "naja");
};
void reifen_bericht(){
delay(4000);
rv->bericht();
lv->bericht();
rh->bericht();
rl->bericht();
};
};
Auto *car;
car = new Auto();
car->initalisiere_reifen();
car->reifen_bericht();
Und Java... naja ehrlich gesagt programmierte ich früher in bash und PHP und seit 20 Jahren verdiene ich meine Brötchen mit ABAP.
C hatte ich mal 2 Wochen 1998 gelernt und seither nichts mehr gemacht und C++ .... well... eigentlich noch gar nichts.
Am schlimmsten find ich eigentlich, dass die ganzen Bücher über OOP (egal ob C++ oder Java) von irgendwelchen Theoretikern geschrieben werden (ja ich hab auch eins und da kräftig nachgelesen, deine Lösung stand nicht drin) und praktische Beispiele oder gar Tipps fürs tägliche Leben da total untergehen
Re: CPP Problem ... ich verzweifel noch
Das ist ein Fehler in den sogar Dozenten in "Grundlagen der Informatik" immer weider reinrennen.
Ich hatte Java unterstellt, weil Java eben genau diesen Unterschied versteckt, wodurch dieser Fehler ständig passiert, wenn man auf die Realität losgelassen wird.
Hier geht das so:
C++ trennt das explizit (wie mir sinnvoll erscheint).
Im Übrigen funktioniert dein String genauso: Es gibt die Klasse String. Mit
Erledigst du gleich alles (aufm Stack) und hast eine vollfunktionsfähige Instanz der Klasse String, die man mal kurz benutzen kann. So wie man es immer mit Basisdatentypen macht.
Es gibt dann auch noch Referenzen, die wie Variablen aussehen, sich aber wie Pointer verhalten, aber hier wäre ich vorsichtig. Da kriegt man schnell nen Knoten im Hirn.
Ich hatte Java unterstellt, weil Java eben genau diesen Unterschied versteckt, wodurch dieser Fehler ständig passiert, wenn man auf die Realität losgelassen wird.
Hier geht das so:
Code: Alles auswählen
int Variable; //Basisdatentyp: Stelle sofort Speicher bereit
Trinitrotoluol Boeller; //Hier wird nur eine Referenz erzeugt!
Im Übrigen funktioniert dein String genauso: Es gibt die Klasse String. Mit
Code: Alles auswählen
String hersteller;
Es gibt dann auch noch Referenzen, die wie Variablen aussehen, sich aber wie Pointer verhalten, aber hier wäre ich vorsichtig. Da kriegt man schnell nen Knoten im Hirn.
Re: CPP Problem ... ich verzweifel noch
Vorsicht mit den Pointern!
Mit new belegst du Speicher der in deinem Beispiel nie wieder freigegeben wird. Normalerweise macht man das entweder im Destruktor oder man verwendet smart pointer(das Objekt wird gelöscht wenn keine Referenz darauf existiert).
Deshalb habe ich shared_ptr vorgeschlagen. Diese Pointer sind seit c++11 Teil von STL ( std::shared_ptr). Ich habe aber überlesen das es um Arduinos geht und daher wirst du STL nicht verwenden können/wollen.
Ich würde die Reifen im Konstruktor erzeugen und dann im Destruktor löschen. Dadurch kann man nicht unabsichtlich reifen_bericht vor initalisiere_reifenauf aufrufen und der Speicher wird korrekt freigegeben. Also:
Mit new belegst du Speicher der in deinem Beispiel nie wieder freigegeben wird. Normalerweise macht man das entweder im Destruktor oder man verwendet smart pointer(das Objekt wird gelöscht wenn keine Referenz darauf existiert).
Deshalb habe ich shared_ptr vorgeschlagen. Diese Pointer sind seit c++11 Teil von STL ( std::shared_ptr). Ich habe aber überlesen das es um Arduinos geht und daher wirst du STL nicht verwenden können/wollen.
Ich würde die Reifen im Konstruktor erzeugen und dann im Destruktor löschen. Dadurch kann man nicht unabsichtlich reifen_bericht vor initalisiere_reifenauf aufrufen und der Speicher wird korrekt freigegeben. Also:
Code: Alles auswählen
class Auto {
private:
// hier sollen jetzt die Reifen (=Objekte) rein für rv, lv, rh, rl
Reifen *rv;
Reifen *lv;
Reifen *rh;
Reifen *rl;
byte dummy;
public:
Auto(){
rv = new Reifen("Bridgestone", "gut");
lv = new Reifen("Dunlop", "gut");
rh = new Reifen("Fulda", "schlecht");
rl = new Reifen("Yokohama", "naja");
};
~Auto(){
delete rv;
delete lv;
delete rh;
delete lh;
}
void reifen_bericht(){
...
};
};
Re: CPP Problem ... ich verzweifel noch
ferdimh/Lukas: Ihr seid echt klasse. Ich glaube, ich such mal nach einem Buch für Praktiker. Für Theoretiker hab ich ja schon eins ;-(
@Lukas: Ja stimmt an das Freigeben hatte ich da gar nicht mehr wirklich gedacht, da in meinem Anwendungsfall alle Klassen genau einmal existieren so lange das Programm läuft. Aber das muss ja nicht immer so sein.
LG, Mathias
@Lukas: Ja stimmt an das Freigeben hatte ich da gar nicht mehr wirklich gedacht, da in meinem Anwendungsfall alle Klassen genau einmal existieren so lange das Programm läuft. Aber das muss ja nicht immer so sein.
LG, Mathias
Re: CPP Problem ... ich verzweifel noch
Es gibt noch eine einfache Lösung, wenn Du keinen dynamischen Speicher verwenden kannst/möchtest:
Zur Erklärung: Das Member-Objekt (_motor) wird nicht bei seiner Deklaration innerhalb der Klasse erzeugt, sondern unmittelbar bevor der {}-Block deines Konstruktors ausgeführt wird. Das nennt sich Initialisierung. Wenn Du das Objekt nicht in der Initialisierungsliste (der Teil hinter dem Doppelpunkt) aufführst, versucht der Compiler, den Default-Konstruktor zu verwenden, den es für die Klasse Motor aber nicht gibt -> Fehler. Wenn Du statt dessen eine Initialisierung angibst, ist alles gut.
Die Sache mit Pointer/dynamischem Speicher kann sinnvoll sein (Stichwort pimpl), aber es hat auch Vorteile, das nicht zu machen. Z.B. hat man nicht auf jedem (embedded) System dynamischen Speicher zur Verfügung. Außerdem wird so das Motor-Objekt direkt in das MyClass-Objekt eingebettet, liegt also zusammenhängend im Speicher. Das kann den Zugriff schneller machen (Cache/Prefetch), außerdem spart man eine Pointer-Dereferenzierung, den Speicherplatz für den Pointer und die Ausführungszeit für das Speichermanagement (new+delete).
Edith ist noch eingefallen: Wenn man schon Pointer verwendet, gibt es mehrere Fallstricke zu beachten. Der erste ist das Kopierverhalten der Klasse (tiefe vs. flache Kopie). Sollen alle Kopien eines Objektes auf die gleiche Instanz der Membervariable zugreifen? Normalerweise wahrscheinlich eher nicht. Was passiert beim Zerstören einer Kopie? Im Zweifel würde ich hier erst mal std::unique_ptr verwenden, wenn nicht explizit ein flaches Kopierverhalten beabsichtigt ist.
Die Variante mit new/delete und mehreren Membervariablen kann zu Speicherlecks führen, wenn in einem der Konstruktoraufrufe eine Exception auftritt. Das ist quasi das Lehrbuch (negativ-)Beispiel, warum man Smart Pointer verwenden soll.
Code: Alles auswählen
class Motor
{
public:
Motor( int Nr);
// ...
};
class MyClass
{
Motor _motor; // Member-Deklaration
public:
MyClass( int Nr )
: _motor( Nr ) // Initialisierungsliste
{
// ...
}
};
Die Sache mit Pointer/dynamischem Speicher kann sinnvoll sein (Stichwort pimpl), aber es hat auch Vorteile, das nicht zu machen. Z.B. hat man nicht auf jedem (embedded) System dynamischen Speicher zur Verfügung. Außerdem wird so das Motor-Objekt direkt in das MyClass-Objekt eingebettet, liegt also zusammenhängend im Speicher. Das kann den Zugriff schneller machen (Cache/Prefetch), außerdem spart man eine Pointer-Dereferenzierung, den Speicherplatz für den Pointer und die Ausführungszeit für das Speichermanagement (new+delete).
Edith ist noch eingefallen: Wenn man schon Pointer verwendet, gibt es mehrere Fallstricke zu beachten. Der erste ist das Kopierverhalten der Klasse (tiefe vs. flache Kopie). Sollen alle Kopien eines Objektes auf die gleiche Instanz der Membervariable zugreifen? Normalerweise wahrscheinlich eher nicht. Was passiert beim Zerstören einer Kopie? Im Zweifel würde ich hier erst mal std::unique_ptr verwenden, wenn nicht explizit ein flaches Kopierverhalten beabsichtigt ist.
Die Variante mit new/delete und mehreren Membervariablen kann zu Speicherlecks führen, wenn in einem der Konstruktoraufrufe eine Exception auftritt. Das ist quasi das Lehrbuch (negativ-)Beispiel, warum man Smart Pointer verwenden soll.