Hallo,
habe mir eine Klasse erstellt für meine Potentiometer, soweit funktioniert das erstmal grundlegend.
Habe aber noch 2 offene Baustellen drin.
In der Klasse rechne mit internen Variablen. Am Ende möchte ich diesen Wert im Sketch haben.
Als Krücke habe ich dazu eine Funktion geschrieben.
int value(); // für Rückgabewert
int pulsweite(); // für Rückgabewert
bekommt man auch ohne Funktion Zugriff auf die Werte?
Dann benötige ich intern eine Filterfunktion. Ist in .h und .cpp deklariert.
Leider meckert der Compiler.
Potentiometer.cpp:39: undefined reference to Potentiometer::Filtern(float&, int, int)' Potentiometer.cpp:40: undefined reference to Potentiometer::Filtern(float&, int, int)'
Das Konstrukturzeichen :: irritiert mich. Die Filterfunktion benötige ich ja nur rein intern.
Potentiometer.h (1.04 KB)
Potentiometer.cpp (1.98 KB)
Potentiometer.ino (398 Bytes)
Du bekommst von außen direkten Zugriff auf interne Variablen wenn du sie public machst. Das nicht zu tun ist aber so ziemlich das erste was man lernt.
Normal hat man Getter- und Setter-Methoden:
int getValue();
setValue(int);
Wenn der Wert nicht von außen gesetzt werden soll, lässt man die Setter-Methode einfach weg. Das ist eine Krücke, sondern völlig normal in OOP. Auch in C# sind Properties nur eine Vereinfachung des gleichen Prinzips.
Dadurch hat man Kontrolle darüber nicht nur ob ein Wert verändert werden kann, sondern wenn ja auch wie. z.B. kann man bei einer Setter-Methode Plausibilitäts- und Wertebereich-Überprüfungen machen.
Das Konstrukturzeichen :: irritiert mich.
Das hat nichts mit Konstruktoren zu tun. :: ist der Scope Operator. Die Methode muss natürlich zur Klasse gehören. Selbst wenn sie private ist
Du bekommst von außen direkten Zugriff auf interne Variablen wenn du sie public machst. Das nicht zu tun ist aber so ziemlich das erste was man lernt.
Stimmt auffallend! Beim Lesen mag es ja vielleicht noch "fast" egal sein, aber beim ungeprüften Schreiben in eine public-Variable kann man sich unter Umständen "heiss" suchen, wo der Fehler im Programm sein mag. So weit ich weiss gibt es keine "ReadOnly"-publics und ich wüsste im Moment auch nicht, wo ein ungeprüftes Schreiben sinnvoll sein kann.
(Vor ein paar Wochen war hier eh schon mal ein ähnliches und heikles Thema, ob eine Klasse mit globale Variablen interagieren kann / darf / soll...)
Man kann sich die Getter-Methoden jedoch ein wenig vereinfachen, indem man eine reine "return xyz"-Methode direkt nach oben in die Klassen-Deklaration verlagert:
Also statt
TClass
{
public:
int value();
private:
int _value;
};
int TClass::value()
{
return _value;
}
schreibt man im Konstruktor direkt:
TClass
{
public:
int value() { return _value; }
private:
int _value;
}
Spart unter Umständen 'ne Menge Mini-Methoden und Tipparbeit
In der *.CPP fehlt bei "void Filtern(..." der Name der Class.
Also:
void Potentiometer::Filtern(float...
schreibt man im Konstruktor direkt:
Du meinst in der Klassen-Deklaration im Header. Auch das hat mit Konstruktoren nichts zu tun. Aber es stimmt: man wird nicht gezwungen Methoden in der .cpp Datei zu definieren. Vor allem bei Einzeilern wird das oft im Header gemacht
Doc_Arduino:
... bekommt man auch ohne Funktion Zugriff auf die Werte?
Wie schon die anderen geschrieben haben, benutzt man für Zugriffe auf Werte (Attribute) einer Klasse sogenannte „setter-“ und „getter-Funktionen“.
Eine der Stärken von Klassen ist ja gerade, dass die Attribute einer Klasse und der Code, der damit hantiert, zusammengefasst und „gekapselt“ sind. Wenn sich Attribute einer Klasse irgendwie seltsam verhalten, weißt Du, dass sich die Ursache irgendwo in den Funktionen (Methoden) der Klasse befindet. Damit kann man nicht selten neun Zehntel seines Programms von der Fehlersuche ausschließen.
Gruß
Gregor
AFAIK werden Methoden-Implementierungen in der Klassendeklaration als "inline" behandelt. Das spart Speicher und Laufzeit.
DrDiettrich:
AFAIK werden Methoden-Implementierungen in der Klassendeklaration als "inline" behandelt. Das spart Speicher und Laufzeit.
Ja, wenn man Methoden schon in der Headerdatei definiert, werden sie „inline“ übersetzt. Es spart halt, weil kein Methodenaufruf veranstaltet wird. Das spart bestimmt Laufzeit, aber nicht unbedingt Speicher (wenn die Methode im Code 50 x aufgerufen wird, wird sie eben 50 x in das Kompilat eingefügt, anstatt sie nur einmal zu übersetzen und 50 x zu referenzieren).
Inline sind bei mir üblicherweise nur die einfachsten Dinge, z. B. Getter- und Setter-Methoden.
Gruß
Gregor
Noch genauer, kann der Compiler sie inline machen. Er wird es i.d.R. tun aber auch das inline Schlüsselwort ist nur eine Empfehlung.
Bei inline Getter und Setter Methoden werden die Variablen der Klasse direkt gelesen bzw. geschrieben, schneller und kürzer geht das nicht. Echte Unterprogramme machen nur beim Debuggen auf einem richtigen Rechner Sinn, wo man Breakpoints setzen kann und dann prüfen, von wo eine Methode aufgerufen wird, und mit welchen Werten.
Hallo,
nachdem über Nacht mein Internet tot war, kann ich heute wieder reagieren.
Konstruktor zur Filterfunktion habe ich hinzugefügt. Danke. Funktioniert. 
Das was Rudi zeigt sind demnach inline Methoden? Wenn es in der Headerdatei schon definiert wird.
Inline spart Rechenzeit, aber nicht unbedingt Speicher. Fällt bei kurzem Methoden aber nicht auf. Richtig verstanden?
Schlüsselwort inline muß man nicht angeben, erkennt der Compiler selbst?
Was ich immer wieder durcheinanderwerfe sind die Begriffe Deklaration und Definition. Damit wird auch in Büchern lustig hin- und her gewürfelt. Was ist nun richtig? Gerade weil viele hier gern mit den richtigen Fachbegriffen um sich werfen.
Im Moment habe ich mir das so gemerkt. In der Headerdatei sind es reine Deklarationen von Elementen und Funktionen. Funktionen und Methoden sind das gleiche.
In der .cpp werden dann die Funktionen ausformuliert und das nennt man dann Definitionen oder Implementierung?
Wenn ich ein neues Objekt erstelle, dann meckert der Compiler wegen den const byte privaten Variablen.
Sollte doch im Grunde nicht stören, deswegen soll er doch ein neues Objekt erstellen mit neuen mitgegeben Werten. In .h Zeile 19 und 20.
Nehme ich das const weg, klappt alles.
Ansonsten muß ich sagen fetzt das schon, wenn man nun halbwegs verstanden hat wie man eine Klasse erstellt. 
Potentiometer.h (1.02 KB)
Potentiometer.cpp (1.72 KB)
Potentiometer.ino (759 Bytes)
Doc_Arduino:
Das was Rudi zeigt sind demnach inline Methoden? Wenn es in der Headerdatei schon definiert wird.
Ja, genau.
Doc_Arduino:
Inline spart Rechenzeit, aber nicht unbedingt Speicher. Fällt bei kurzem Methoden aber nicht auf. Richtig verstanden?
Schlüsselwort inline muß man nicht angeben, erkennt der Compiler selbst?
Ja und ja.
Doc_Arduino:
Was ich immer wieder durcheinanderwerfe sind die Begriffe Deklaration und Definition. Damit wird auch in Büchern lustig hin- und her gewürfelt. Was ist nun richtig? Gerade weil viele hier gern mit den richtigen Fachbegriffen um sich werfen.
Im Moment habe ich mir das so gemerkt. In der Headerdatei sind es reine Deklarationen von Elementen und Funktionen. Funktionen und Methoden sind das gleiche.
In der .cpp werden dann die Funktionen ausformuliert und das nennt man dann Definitionen oder Implementierung?
Das mit Deklaration und Definition hast Du wohl richtig verstanden. Die Deklaration ist das, womit man dem Compiler sagt, was für eine Funktion noch kommt - so wie man z. B. das Fass mit dem Rum beim Zoll deklariert (oder nicht :-). Die Definition ist dann der Code, der ausgeführt wird, wenn die Funktion aufgerufen wird.
Doc_Arduino:
Ansonsten muß ich sagen fetzt das schon, wenn man nun halbwegs verstanden hat wie man eine Klasse erstellt. 
Ich habe die „Klassen-Denke“ so verinnerlicht, dass ich mich bei neuen Programmen immer zuerst frage, womit ich hantieren will und wie das in Klassen passt.
Gruß
Gregor
Na suuuuuper...
nun logge ich mich gerade euphorisch ein um Doc_'s letzten Beitrag nett zu kommentieren.. und Herr Gregor war nahezu exakt mit "meinem" Text schneller... baaahhh, und dann noch die Sache mit dem Fass Rum! Okay, ich trinke zwar nix "hartes" aber Durst bekomme ich trotzdem 
Aber ich schicke dann noch einen Satz hinterher: Wie @Serenifly schon sagte UND im Dokument geschrieben steht welches dir vorliegt, hat der Compiler die Freiheit (Frechheit?) zu entscheiden, ober er es "inline" macht oder "als Funktion"
Na ja, das "Denken in Klassen" verinnerliche ich auch immer mehr, sogar eine dumme blinkende LED packe ich schon in eine Class, das trainiert ungemein! So, jetzt begebe ich mich wieder daran, mit Structs zu jonglieren... 
Hallo,
sehr gut, dann habe ich das nun so langsam gerafft. Zumindestens das Grundgerüst.
Vielen Dank an alle Beteiligten. War eine schwere Geburt - ich weis. 
Eine Frage bleibt noch. Ich muß den Joystick noch kalibrieren, min, max, mitte.
Dazu benötige ich einen Taster und am besten mein Display.
Wenn ich nun meine bisheren Funktionen/Code dafür in die Klasse "werfe", dann ist die Klasse sehr starr.
Im Grunde genommen dann nur für das bestimmte Projekt verwendbar.
Wie macht ihr das?
Programmiert ihr Klassen nur für das eine euer Projekt oder versucht ihr allgemein zu bleiben für andere Projekte oder paßt ihr die Klassen dann für jedes Projekt neu an?
Na ja, ich denke mal, das hat vielleicht etwas mit "geschicktem Software-Design" zu tun oder so ähnlich. Eine Class ständig den Anforderungen anzupassen ist sicherlich nicht im Sinne des Erfinders. Ich kann dir nur aus meiner eigenen Erfahrung erzählen, dass ich mir (meistens...) vorab Gedanken darüber mache, wie flexibel und/oder umfangreich eine einzelne Class sein müsste: Nur jene Methoden in eine Class packen, die wirklich zusammen gehören. Später wenn man dann mehrere "in sich geschlossene" Classes hat spricht ja nichts dagegen, diese so zu kombinieren, so dass sie dann in der Lage sind optimal miteinander zu kommunizieren ("Botschaften austauschen"). Aber das bringt wohl erst der Lauf der Zeit und die Erfahrung.
Hallo,
gut, dann werde ich die Kalibrierung erstmal in ein Tab auslagern. Das Kuttelmuttel mit dem Taster, Display und Sonderzeichen gehören vielleicht auch nicht direkt in die Poti Lib. Ich lern ja noch.
bis denne
Doc_Arduino:
Wie macht ihr das?
Programmiert ihr Klassen nur für das eine euer Projekt oder versucht ihr allgemein zu bleiben für andere Projekte oder paßt ihr die Klassen dann für jedes Projekt neu an?
Wie man „richtig“ programmiert, ist ein „running-Streitpunkt“ von einem Kumpel und mir. Er macht vielleicht alles richtig, ich mach‘s ganz sicher richtiger :-))
Wie ich programmiere, hängt davon ab, ob ich die Möglichkeit sehe, etwas wiederverwenden zu können. Ein paar Kleinigkeiten habe ich mir so programmiert, dass ich es quasi in allen Programmen verwenden kann (z. B. eine Klasse, mit der ich Konfigurationsoptionen „verwalte“ oder eine Klasse, mit der ich Pixelbilder malen kann [Bsp.]). Dass ich Code wiederverwenden konnte, kam auch schon oft vor. Voraussetzung ist halt, dass der Code so allgemein gehalten ist, dass er ohne Anpassung anderswo nützlich sein kann.
Gruß
Gregor
Meine Eselsbrücke: Deklaration kommt vor Definition (a vor i).
Man kann auch Definition und Implementierung gleichsetzen.
Hallo,
okay, kommt immer auf den Anwendungsfall an. Ist ja meine erste eigene Lib.
Hauptsache Code ist gekappselt, raus aus dem Hauptsketch und funktioniert.
Die Kalibrierwerte übergebe ich dann mit einer Setter Methode, um mal den Fachbegriff zu verwenden. 
Schöne Eselsbrücke. 
Und die Deklaration nimmt uns die IDE im Hauptsketch ab. Deswegen fiel mir das auf die Füße bzw. die Einsortierung fiel mir schwerer.
Konstruktor zur Filterfunktion habe ich hinzugefügt. Danke. Funktioniert
Bist du verwirrt weil der Konstruktor wie die Klasse heißt? Was du da hinzufügst ist der Name der Klasse. Das hat nichts mit Konstruktoren zu tun