ich steige gerade in das Thema Klassen ein. Leider liegt mir das derzeit gar nicht, will es aber versuchen. Hab mir dazu einige Seiten und Videos angesehen. Auf der folgenden Seite finde ich das ganz gut erklärt: https://spacehal.github.io/docs/arduino/objectOriented
1
public:// alle Methoden (oder Attribute) nach public sind öffentlich
Wieso wird "pinLed nicht direkt in den Konstruktor geschrieben, sondern "pPin" und dann direkt an die pinLed var. übergeben? MUSS das so oder kann man das so machen? Welche Vorteile gibt es, wenn es nicht notwendig ist?
pinLed sollte eigentlich nach der Initialiserung durch den Konstruktor nicht mehr verändert werden können, weil im laufende Betrieb niemand die LEDs umlötet.
Also kann das const sein, was dann die erste Art der Initialisierung im Konstruktor-Rumpf nicht mehr erlaubt.
Der initiale Wert für stateLed lässt sich dann auch gleich bei der Deklaration angeben, und wenn man dann noch das undefinierte pin in pinLED ändert, sieht es so aus:
1
classLed{
2
constintpinLed;
3
boolstateLed{false};
4
5
public:
6
Led(intpPin):pinLed(pPin){
7
pinMode(pinLed,OUTPUT);// Pin als Ausgang definieren
Da fehlt ein Semikolon!
Zudem hat pinMode() nichts im Konstruktor zu suchen.
Bei AVR vielleicht unkritisch.
Aber bei anderen Architekturen ist noch nicht gewährleistet, dass zu dem Zeitpunkt der pinMode() seine Wirkung entfalten kann.
Darum haben auch alle Klassen in der Arduino Welt (die sowas tun müssen) eine begin() oder init() Methode.
Man kann auch mit einer constructor member initializer list arbeiten, aber der Computer hat letztlich immer genau dasselbe zu tun: den als Parameter übergebenen Wert in die Klassenstruktur zu kopieren.
So bald man Exceptions in einer Programmiersprache ermöglichen will
1
voidprocessing(char*ptr)
2
{
3
// tue irgendwas
4
}
5
voidshit(intx)
6
{
7
if(x>20)throw"Bullshit";
8
}
9
voidtest(inty)
10
{
11
try
12
{
13
char*leaked=strdup("Something");
14
shit(y);
15
processing(leaked);
16
free(leaked);
17
}
18
catch(char*message)
19
{
20
puts(message);
21
}
22
}
23
intmain(void)
24
{
25
test(22);
26
}
hat man ein Problem mit dynamisch allokiertem Speicher auf dem heap, hier leaked welcher bei einem Fehler in shit nicht mehr freigegeben wird.
Man braucht Variablen die bei einem stack unwind, also Verlassen des Scopes, ihre Daten selbst freigegeben können. Man braucht also Variablen mit Codeergänzung, vor allem Destruktoren.
Eine Klasse ist eine struct Struktur mit Funktionen die einen versteckten self Patameter auf ein Element diesen Strukturtyps beinhalten.
1
structstring
2
{
3
char*data;
4
}
5
assign(structstring*self,char*value)
6
{
7
self->data=strdup(value);
8
}
9
destroy(structstring*self)
10
{
11
free(self->data);
12
}
ist dann
1
classstring
2
{
3
char*data;
4
string(char*value)
5
{
6
data=strdup(value);
7
}
8
~string()
9
{
10
free(data);
11
}
12
}
mit dem Zusatzeffekt, dass ~string automatisch aufgerufen wird, wenn so eine Variable im stack unwind freigegeben wird, also
1
voidprocessing(char*ptr)
2
{
3
// tue irgendwas
4
}
5
voidshit(intx)
6
{
7
if(x>20)throw"Bullshit";
8
}
9
voidtest(inty)
10
{
11
try
12
{
13
stringnotleaked("Something");
14
shit(y);
15
processing(notleaked);
16
}// bei verlassen dieses Blocks egal ob normal oder wegen exception wird ~string von notleaked automatisch aufgerufen und gibt den Speicher auf den data zeigt frei
17
catch(char*message)
18
{
19
puts(message);
20
}
21
}
22
intmain(void)
23
{
24
test(22);
25
}
Alles andere (Vererbung etc.) ist dann nur Zusatznutzen oder -ärger.
Der Kern sind aber Klassen = eine struct aus Daten zusätzlich mit Funktionen mit verstecktem self Parameter die dann eben nicht Funktionen sondern Methoden heissen.
Jeder Fehler weniger bringt einen der Lösung näher!
Dann würde ich noch auf die gute, alte Regel aufmerksam machen wollen, nach der jeder c'tor der nur ein nicht default Argument hat und nicht zur impliziten Konvertierung dienen soll, als explicit markiert werden sollte:
1
explicitLed(constbytepPin)...
Ansonsten könnte der Compiler den c'tor verwenden, um implizite Konvertierungen vor zu nehmen. Dieses Beispiel würde übersetzen obwohl man ggf. lieber einen Fehler vom Compiler hätte:
Das Drama ist eigentlich, dass die begin() Methode in dem Fall nicht ausgeführt wird.
Macht es nicht Sinn dort eine Referenz zu übergeben?
void light_show(Led &light)
Dann bekommst du auch deine Meldung.
Und ja, explicit ist hier durchaus Sinn behaftet!
Wieder eine/zwei potentielle Fehlerquellen ausgemerzt.
Die Welt wird so gerettet (ein Stückchen besser), quasi zum Nulltarif
Man kann auch mit einer constructor member initializer list arbeiten,
aber der Computer hat letztlich immer genau dasselbe zu tun: den als
Parameter übergebenen Wert in die Klassenstruktur zu kopieren.
Das kommt darauf an, was man will. Aber im Normalfall muss man das in der initializer-list des ctors machen.
Und gleich wird er uns seine portable ASM Lösung vorstellen
Moby hat hier noch nie jemandem geholfen. Nur geprahlt. Dabei gibt (und gab) es genügend Fragen/Probleme in diesem Forum zu Assembler, die bis heute ungelöst sind. Nein: Keines davon hat Moby jemals beantwortet oder gelöst. Typischer Blender halt.
Was hat das hier im Kontext von C++ zu suchen?
Es dient nur dazu, damit Du Dein Wissen über exceptions hier einbringen
kannst.
Oje Wilhelm, es ist ein Beispiel um zu zeigen warum man Klassen braucht.
Wenn du es nicht verstanden hast, schade.
Man kann dort auch beliebigen anderen Kram hinschreiben der irgendwas auf dem heap alloziert, mir fiel nichts kürzeres ein, was für jemanden dem nur C bekannt ist, unmittelbar verständlich ist.
Exceptions sind halt DER Grund warum man Klassen braucht. Klassen nur der Klassen wegen sind kein Grund, sondern höchstens ein nice to have.
Wobei es verdammt viele Programme gibt, die durch Klassen schlechter verständlich und schlechter wartbar und in der Performance schlechter wurden.
Alles andere (Vererbung etc.) ist dann nur Zusatznutzen oder -ärger.
… Vererbung - und insbesondere darauf basierend Polymorphie.
Exceptions sind halt DER Grund warum man Klassen braucht. Klassen nur
der Klassen wegen sind kein Grund, sondern höchstens ein nice to have.
Klassen nutzt man, um die objektorientierte Denkweise umzusetzen. Wenn du das als "Klassen nur der Klassen wegen" betrachtest und den Mehrwert von Vererbung nicht verstanden hast, dann solltest du dir das nochmal anschauen. Sich jetzt gerade Exceptions als den einzigen Grund für Klassen auszusuchen, ist etwas seltsam.
Wobei es verdammt viele Programme gibt, die durch Klassen schlechter
verständlich und schlechter wartbar und in der Performance schlechter
wurden.
Und vermutlich genau so viele, die durch Klassen übersichtlicher und deutlich besser wartbar sind. Die Verwendung von Klassen bedeutet auch nicht automatisch eine schlechtere Performance.
Die Verwendung von Klassen bedeutet auch
nicht automatisch eine schlechtere Performance.
Das ist zwar theoretisch tatsächlich so, praktisch passiert aber genau dies fast immer. Der Fluch der Kapselung. Noch ohne jede Vererbung: Der Code der Klasse muß jederzeit den Datenbestand der Klasse konsistent halten, egal, welche der Mehoden in welcher Reihenfolge aufgerufen wird. Allein das impliziert schon fast immer einen gewissen Overhead.
Wenn dann aber noch Vererbung und insbesondere polymorphe Methoden hinzukommen, ist der Overhead sogar garantiert. Das ist theoretisch beweisbar (und natürlich vor Jahrzehnten bereits formal bewiesen worden).
Das ist halt das, was OOP in Kauf nimmt, um leicht verwendbare Objekte bieten zu können, die auch eine relativ hohe Sicherheit gegen Fehlbenutzung haben. Auf Perfomance war das OOP-Konzept niemals optimiert.
Aber das Hauptproblem ist wohl: diese Objekte sind ZU leicht zu verwenden. Keiner der "Programmierer", die Objekhierarchien Fremder benutzen, beschäftigt sich noch damit, was die benutzte Methode jeweils kostet und ob man das, was man letztlich will, nicht auch billiger haben könnte, selbst unter Benutzung desselben Objekts...
Das Problem ist also nicht OOP an sich, sondern unfähige "OO-Leecher". Früher(tm) habe ich die im Delphi-Umfeld "Komponentenschubser" genannt. Nunja, Delphi interessiert heute fast niemand mehr, aber der Typ des unfähigen Komponentenschubsers existiert wohl in jeder OO-Sprache und OO-Umgebung. OO-Systembedingt...
Und genau das ist, was OO-Anwendungen immer und immer langsamer macht...
Der Trend geht aktuell von der OOP eher weg, siehe Rust. Da gibt es keine Vererbung. C++ bietet Mehrfachvererbung und da kommt man schnell in Teufels Küche. Exceptions werden auch nur genutzt, wenn es wirklich notwendig ist. Da kommt nämlich ein Overhead rein. Sonst macht man das wie in C mit irgendwelchen return codes. Mittlerweile gibt es ja std::optional das kommt ja schon nahe an Rust heran.
Und vermutlich genau so viele, die durch Klassen übersichtlicher und
deutlich besser wartbar sind.
Sicher nicht genau so viele, sondern die sind die Ausnahme.
Es gibt saubere Lehrbeispiele. Aber wenig reales Zeug dass mit so viel Mühe durchprogrammiett wurde.
Die Verwendung von Klassen bedeutet auch
nicht automatisch eine schlechtere Performance.
Jein, das Mitschleppen von self kostet IMMER Performance. Die müsste man durch Nutzung von 'features' wieder reinholen. Eines dieser features wären exceptions
Der primäre Grund, warum es Klassen gibt, ist aber nicht, um Exceptions
zu unterstützen, sondern …
Michael B. schrieb:
Alles andere (Vererbung etc.) ist dann nur Zusatznutzen oder -ärger.
… Vererbung - und insbesondere darauf basierend Polymorphie.
Auch da würde ich widersprechen: nicht Vererbung als Technik für Inklusionspolymorphie ist der hauptsächliche Beweggrund sondern die Kapselung und Abstraktion. Vererbung generell wird meistens überbewertet und nur Anfänger versuchen, alles in eine Klassenhierarchie zu pressen. Komposition ist wichtiger, und damit wieder die Kapselung und Abstraktion.
Noch ohne jede Vererbung: Der
Code der Klasse muß jederzeit den Datenbestand der Klasse konsistent
halten, egal, welche der Mehoden in welcher Reihenfolge aufgerufen wird.
Allein das impliziert schon fast immer einen gewissen Overhead.
Das suggeriert, dass man ohne OOP ganz locker mit Inkonsistenzen leichter leben könnte. Und das ist natürlich ab einer bestimmten Größe des SW-Artefaktes Blödsinn.
Exceptions sind halt DER Grund warum man Klassen braucht.
Nö: klares Nein.
Deine Argumentationskette ist umwerfend und so stichhaltig überzeugend.
Ja, weil Deine Aussage einfach (im Wortsinne) falsch ist. Den Rest kann man überall nachlesen, man starte bei Wikipedia. Das muss ich hier nicht wiederholen.
Ja, weil Deine Aussage einfach (im Wortsinne) falsch ist. Den Rest kann
man überall nachlesen, man starte bei Wikipedia. Das muss ich hier nicht
wiederholen.
Du hast nicht mal nachgedacht, sondern plärrst stumpf das nach was du Mal aufgeschnappt hast nach. Armes Herdentier, bleibt dumm wie Brot.
Versuche mal herauszufinden, welche Abhangigkeiten es gibt und welche notwendige Vorbedingungen sind.
Als Ubung: warum Atom und Region der Kern von Windows sind.
Ja, weil Deine Aussage einfach (im Wortsinne) falsch ist. Den Rest kann
man überall nachlesen, man starte bei Wikipedia. Das muss ich hier nicht
wiederholen.
Der
Code der Klasse muß jederzeit den Datenbestand der Klasse konsistent
halten, egal, welche der Mehoden in welcher Reihenfolge aufgerufen wird.
Übersetzt und den Hasser Bias da mal raus genommen:
Der Code der Klasse kann jederzeit den Datenbestand der Klasse konsistent
halten.
Ja, das ist doch fantastisch!
C und ASM Anwender können nur von sowas träumen.
Oder müssen das mit viel Aufwand nachstricken, und es wird nur halb so gut.
Übrigens:
Kein Wort glaube ich deiner negativen Propaganda.
Auch weil sich irgendwann herausgestellt hat, dass du mindestens 11 Jahre in der C++ Entwicklung verschlafen hast.
Interessieren würde mich mal, ob du dir jemals die Einarbeitungsphase, von min. 1/2 Jahr, angetan hast. Ob deine Gehirnwindungen C++ kompatibel sind. Ich meine, ob du überhaupt in der Lage bist, dich den, z.B. OOP, Konzepten positiv zuzuwenden.
Zu allen deinen Punkten kann man sagen:
Wer will, findet Wege.
Wer nicht will, (er)findet Gründe.
Beispiel:
C-hater schrieb im Beitrag #7459481:
Keiner der "Programmierer", die Objekhierarchien Fremder
benutzen, beschäftigt sich noch damit, was die benutzte Methode jeweils
kostet und ob man das, was man letztlich will, nicht auch billiger haben
könnte, selbst unter Benutzung desselben Objekts...
Erstens: Da steckt eine Pauschalisierung drin "keiner". Pauschalisierungen haben bestenfalls eine Wahrscheinlichkeit, eine Trefferwahrscheinlichkeit.
So auch eine Falschwahrscheinlichkeit. Die Ansage ist also in ihrer Absolutheit falsch.
Es kann schon sein, dass du deine offensichtliche Ablehnung auf solchen absolut Behauptungen aufbaust, um dich selber zu überzeugen. Würde es zumindest halbwegs erklären.
Tipp:
Je öfter du dir das vorbetest, desto fester glaubst du selber daran.
Das suggeriert, dass man ohne OOP ganz locker mit Inkonsistenzen
leichter leben könnte.
Nein, das ist natürlich nicht so.
Es ist vielmehr so: da es ohne OOP die Garantie nicht gibt, dass der Datenbestand bei beliebiger Aufrufreihenfolge der Methoden (dann also keine Methoden, sondern Funktionen) konsistent bleiben wird, kann man einfach eine bestimmte Reihenfolge VORSCHREIBEN.
Also z.B. sowas (sehr einfacher Fall): "bevor irgendeine andere xyz_-Funktion aufgerufen werden darf muß einmalig xyz_init aufgerufen werden".
Die Crux des Objektes ist halt: das muss das selber prüfen, in aller Regel zur Laufzeit. Das kostet Zeit und Codespace. Das Nicht-Objekt mit derselben Funktionalität kann auf diese Prüfungen verzichten, da die Einhaltung der Restriktionen dem Benutzer obliegt.
Kannst du dir genauso vorstellen, wie die UB-Scheiße in C/C++. Auch hier wird der Benutzer dazu verdonnert, einfach alles richtig zu machen...
Ich habe wahrscheinlich schon OOP betrieben, da warst du noch das
lüsterne Glitzern in den Augen deines Vaters.
Konkret: Anfang der 1990er.
Cool, voll der Experte.
1990 war Smalltalk schon 18 Jahre erwachsen, aber du weisst sicher nicht dass diese das Paradebeispiel für Objektorientierung war. 1990 war es schliesslich eher wieder out, weil die Leute spätestens als es 1980 widespread wurde langsam merkten, dass man damit keine konkurrenzfähigen Programme schreiben konnte.
Der Punkt ist nicht, wie lange man glaubt, OOP zu machen, oder zu wissen, wann das Paradigma mit welcher Sprache entwickelt wurde -- nebenbei: das war nicht Smalltalk. Der Punkt ist vielmehr, ob man die Objektorientierung verstanden habt -- und solltet Ihr beide das haben, habt Ihr es zumindest vor mir sehr erfolgreich verborgen.