Noch mehr Hilfe fürs Verstehen von C++ gesucht

Thomasio

Erfahrenes Mitglied
Nachdem ich mich gerade durch den Thread von Marco7757 mit gleichem Titel gelesen habe, finde ich mich mal wieder vor meinem grössten Problem wieder.
Ich habe gleich mehrere Dutzend Male versucht irgendwas in OOP zu schreiben, bin jedesmal kläglich gescheitert und immer wieder zu .... nennen wir es mal .... meiner Eigenkreation von Programm zurück gekehrt, Spaghetti-Code mit Dutzenden von globalen Variablen.
Ich weiss, dass das lächerlich ist, ich weiss, dass ich gegen sämtliche Ratschläge sämtlicher Tutorials verstosse, ich weiss, ich weiss, ich habe nur 2 Gründe, warum ich das trotzdem so mache:

1) Ich kapiere einfach nicht, wie man das selbe Ergebnis in OOP auf die Beine stellen könnte.

2) Ich schreibe ausschliesslich hobbymässig, nur um zu sehen, ob ich es schaffe, ohne dass ich mit dem Ergebnis hinterher etwas anfangen könnte.
Z.B. habe ich einen kleinen Chat-Server geschrieben, wo User sich in Räumen treffen und chatten, sowie private Nachrichten austauschen können.
Natürlich ist das vollkommen sinnfrei, weil es sowas in professionellen Versionen zu 100ten im Web gibt, ich wollte nur rausfinden, ob ich sowas auf die Beine stellen kann, und ich habe das Ganze direkt nachdem ich es fertig hatte zu den Akten gelegt.

Ich würde sonstwas drum geben, wenn ich irgendwie OOP verstehen lernen könnte, aber genau wie Marco im anderen Thread komme ich von PHP, und selbst das habe ich vor so langer Zeit gelernt, als von OOP noch kein Mensch gehört hatte.
In Spaghetti-Code bringe ich 200 000 Zeilen in eine einzige Source-Datei, ich habe keine Probleme bei 500 globalen Variablen die Übersicht zu behalten (ich weiss immer noch, dass das Unfug ist), aber wenn ich mich in OOP versuche, scheitere ich meist schon innerhalb der ersten 10 Zeilen.

Genau wie Marco gesagt hat, wenn ich ein Buch oder Tutorial zu OOP lese, dann langweile ich mich durch 100te von Seiten wo Datentypen und Zeug erklärt werden, aber sobald es an OOP geht verstehe ich nur noch Bahnhof.

Kann mir irgendwer irgendwas empfehlen, wie ich mich da einarbeiten könnte?
 
Hi,
also ich vermute, dass dir einfach die Übrsicht dazu fehlt, was genau OOP ist.
Ich würde dir Empfehlen diverse kleine Projekte bzgl OOP zu bauen, damit du für größere Projekte damit "warm" bist.

Als Beispiel, wenn du dein Chat-Client hast, dann bastel dir eigene Klassen für Verbindungsaufbau, den Chat, Menü usw.
Versuche deinen Code in sinnvolle "Einzelobjekte" einzuteilen. Hier nochmal das Stichwort "Verbindungsaufbau", ich weiß nicht genau wie du es realisiert hast, aber ich habe mir eine extra Klasse dafür geschrieben (mit Sockets), damit ich in verschiedenen Programmen diesen Code verwenden kann. Das ist ja gerade auch der Sinn am objektorientierten. Der Code soll in einzelne Objekte ausgelagert werden, die zusammengesetzt dein programm ergeben. So müsstest du ggf nur einmal eien Funktion aufrufen, die dir den ganzen Verbindungsaufbau realisiert.

Ein weiteres Beispiel, was dir vielleicht beim OOP helfen könnte. Versuch doch mal eine Klasse Matrix und eine Klasse Vektor zu erstellen und dann ein Programm, welches mit beiden Objekten arbeiten (rechnen) kann. Danach solltest du sicher einiges mehr verstanden haben.

Dazu kann man dann auch gezielt im Internet gucken und muss sich nicht durch ein Buch wälzen.

Ich hoffe ich konnte dir ein bisschen helfen.

mfg Jennesta

Achja: PHP hat inzwischen auch die möglichkeit für OOP, der Sinn des ganzen ist ja derselbe, vielleicht hast du dort auch diverse Möglichkeiten alte Projekte umzuschreiben.
 
Ich habe eine ziemlich genaue Vorstellung davon was OOP ist.
Teile von Programmen die in sich abgeschlossen sind werden vom Rest abgeschottet, mit dem Sinn, dass man immer nur einen kleinen Teil überschauen muss, und so z.B. gleiche Variablennamen keine Konflike auslösen, solange sie in verschiedenen Klassen stehen.

Aber genau da liegt mein Problem, ich sehe da keine abgeschlossenen Einheiten, ich sehe nur Overhead.
Eine Funktion einer Klasse aufrufen um an den Wert einer Variablen zu kommen, da ist eine global zig1000fach schneller.
Ich verstehe, dass man Konflikte vermeiden kann, aber ich sehe effektiv nur einen gewaltigen Umstand, ein vielfaches an Code in einem vielfachen von Dateien.
Ich schreibe nun mal nicht 10 chat server, in denen ich eine Socket Klasse wiederverwenden könnte.
Ich sehe ein vielfaches an Lernaufwand, weil ich zusätzlich zum Verständnis wie ein send() oder recv() funktioniert (damit ich die Klasse überhaupt mal schreiben kann) auch noch lernen muss in welche Klasse ich sie gepackt habe, wie die Funktion in meiner Klasse heisst und welche Parameter sie erwartet.

In kurz:
Was ist ein sinnvolles Einzelobjekt?
Am praktischen Beispiel:
Wenn ich in meinem Chatserver am Serverende jeden User in einem eigenen thread laufen habe, dann sehe ich noch nicht mal, wie so ein thread ein Einzelobjekt sein kann, weil ich jedes Mal wenn ein neuer User einloggt nicht nur dessen Nick und Passwort prüfen muss, sondern ihm auch noch die Liste aller Nicks aller Anwesenden schicken muss.
Das ist total einfach, wenn ich alle User in einem globalen vector habe, aber in OOP? Wo ist da eine Einheit? Ich sehe nur, dass ich statt einem Loop durch alle User jetzt einen solchen Loop innerhalb einer Klasse bauen müsste, und dann noch zusätzlich einen Funktionsaufruf brauche, der diese Funktion innerhalb seiner Klasse aufruft.
Wenn ein User etwas in den Chat schreibt und ich das an alle anderen User weiterleiten will, wie soll ich davon eine abgeschlossene Einheit machen?

Dabei habe ich zu allererst ein ganz anderes Problem, denn so weit komme ich gar nicht erst, bei mir kommen die Probleme schon in den ersten 10 Zeilen auf.
Ich bringe es nicht mal fertig eine Klasse in eine separate Datei zu schreiben, ohne dass mein Compiler sich über "nicht definiert" beschwert.
Um ein Programm aus mehreren Source-Dateien zu bauen braucht man offensichtlich Dutzende von überkreuzten includes, alle in ifndef eingepackt, damit nichts doppelt geladen wird und schon an der Stelle verliere ich regelmässig die Übersicht.

Bevor wir uns falsch verstehen, ich suche gar nicht nach einer Lösung für dieses spezielle Problem, ich suche nach einem Buch oder Tutorial, wo OOP so erklärt wird, dass einer der Spaghetti-Code auswendig kann, aber von OOP wirklich keinen Dunst hat, die Unterschiede begreift, ohne dass es mich auf den ersten 100 Seiten mit den unterschiedlichen Typen von Variablen und deren Handhabung langweilt.
Mir braucht keiner zu erklären, was eine Vorwärts-Deklaration ist, aber ich würde zu gerne verstehen, wie ich in 27 Source-Dateien eine überschaubare Struktur von Deklarationen und includes hin bekomme.
 
Ich denke OOP kann man in wesentlichen mit zwei Prinzipien erklären: 1.) Kapslung 2.) Abstraktion

Kapslung:
Du trennst die Implementierung von der Schnittstelle. Das benutzt du ohnehin schon. Z. B., wenn du cout benutzt. Dich interessiert in dem Moment nicht, wie cout genau implementiert ist, sondern wie du es benutzen musst, um ein von dir gewünschtes Ergebnis zu erhalten (in diesem Fall die Ausgabe). Du trennst also die Implementierung (vgl. protected/private) von der Schnittstelle (vgl. public). Dadurch verbirgst du gewissermaßen die Details vor dem Benutzer, dem du eine Schnittstelle zur Verfügung stellst. Dieser geht einfach mal davon aus, dass, wenn er z. B. zwei Objekte addieren will, der +-Operator entsprechend implementiert ist.

2.) Abstraktion:
Du versuchst das Konzept soweit zu abstrahieren, dass es für ein möglichst großes Kontingent an Möglichkeiten nützlich ist. Bsp.: Anstatt einfach eine Klasse nur für Autos und eine extra für LKWs zu machen, kannst du auch eine Klasse Straßenfahrzeuge machen, die dann wiederum von einer Klasse Fahrzeuge abgeleitet sein könnte (dazu später mehr). Dieses Prinzip der Abstraktion hat viel zu tun mit Generik (Typunabhängigkeit, vgl. z. B. Container (Stichwort: templates, Bsp: std::vector<int> und std::vector<string>)). Dabei werden immer gewisse Anforderungen an die Typen gestellt, die aber möglichst allgemein gehalten werden (z. B. durch das imkrementieren (++) des 1. Parameters muss der 2. erreichbar sein), die genaue Implementierung ist dabei wieder außen vor.


Ein wichtiges Thema im Bereich der OOP ist sicherlich auch die Vererbung. Dabei gibt es entscheidende Unterschiede zwischen einer is-a und has-a Beziehung.
Beispiel:
Ein Auto erbt von einer Klasse Fahrzeug (is-a), aber enthält nur 4 Objekte der Klasse Rad (has-a), erbt also nicht von dieser.
Mit Polymorphismus lassen sich interessante Dinge anstellen. Wenn wir z. B. eine Funktion haben, die fahre() heißt, so können wir dieser Funktion einen Zeiger auf Fahrzeug übergeben (der Basisklasse von LKW und PKW). In der Funktion sagen wir dann obj->fahre(), wobei obj der Zeiger auf Fahrzeug ist. Die Funktion fahre() wird dann entweder für LKW oder PKW aufgerufen. Die Funktion fahre() ist also in PKW oder LKW überschrieben (es gibt bereits in Fahrzeug eine solche Funktion, diese wird aber nur dann aufgerufen, wenn sie nicht überschrieben wurde. Dazu muss die Funktion als virual gekennzeichnet sein).


Klassen und Vererbung sind also grundlegend für die OOP. Was ist also eine Klasse? Eine Klasse ist eigentlich nur der Typ eines Objekts. Wir können z. B. eine Klasse Auto haben. Von dieser Klasse Auto erstellen wir ein Objekt, z. B. Auto car1; Eine Klasse sieht z. B. so aus:
class Auto : public Fahrzeug
{
//autospezifisches
};

Dabei erbt Auto von Fahrzeug, erhält also deren Schnittstelle dazu, kann virtuelle Funktionen überschreiben etc. Was macht das public vor Fahrzeug? Es besagt nur, dass der Zugriff auf die Funktionen/Variablen vom Fahrzeug-Teil nicht weiter eingeschränkt werden. Stünde hier private, so wäre die komplette Schnittstelle nur für Funktionen der Klasse Auto, nicht aber von außen zugänglich. Bsp: Wenn wir public erben, können wir sagen car1.func() (func() ist eine öffentliche Member-Funktion von Fahrzeug), erben wir private, können wir das nicht. wir können aber sagen:
Code:
Auto::do()
{
func();
}

Es ist also also ob alle öffentlichen Funktionen von Fahrzeug private Funktionen von Auto sind.


Es gäbe jetzt noch eine Unmenge mehr, die dich noch mehr verwirren würde ;) Frag einfach nach, wenn du was nicht verstehst. Ich habe mir das selbst beigebracht, ich übernehme keine Garantie für die Richtigkeit und bitte daher, ggf. Sachen zu verbessern :)


Gruß,

badday


EDIT:

Ich habe eine ziemlich genaue Vorstellung davon was OOP ist.
Teile von Programmen die in sich abgeschlossen sind werden vom Rest abgeschottet, mit dem Sinn, dass man immer nur einen kleinen Teil überschauen muss, und so z.B. gleiche Variablennamen keine Konflike auslösen, solange sie in verschiedenen Klassen stehen.
Falsch, Klasse != Namensbereich


Das ist total einfach, wenn ich alle User in einem globalen vector habe, aber in OOP?
Na, du sprichst von User. User könnte nun eine Klasse sein. Diese Klasse kapselt die Funktionalität (z. B. hat sie die Funktionen ping(), trennen() etc.). Das ist Objekt-orientierte Programmierung.

Um ein Programm aus mehreren Source-Dateien zu bauen braucht man offensichtlich Dutzende von überkreuzten includes, alle in ifndef eingepackt, damit nichts doppelt geladen wird und schon an der Stelle verliere ich regelmässig die Übersicht.
Man verwendet meist Header-Wächter, ja. Aber das sollte erstmal unnötig sein. Beispiel:

Header:
Code:
#ifndef unserersterheader
#define unserersterheader

class A
{
public:
void do()
};

#endif

Source:
Code:
#include "header.h"
#include <iostream>

void A::do()
{
std::cout<<"Wunderbar!"<<std::endl;
}

Source (main.cpp):
Code:
#include "header.h"

int main()
{
A a;
a.do();

return 0;
}


Buch: C++ Primer
 
Zuletzt bearbeitet:
Ähem, ja, schön, oder sollte ich sagen: Bahnhof?

Bleiben wir mal bei deinem Beispiel mit den Autos.
Ich baue also eine Klasse Fahrzeuge (was ich auch erstmal lernen müsste), dann bastel ich da tolle Funktionen rein, ala fahren(), parken() und sonstwas.
Dann verkaufen wir das Programm an eine Leihwagenfirma, die damit ihren Fuhrpark verwalten will, und das Erste, was der fragt: Wenn ein Kunde ein rotes Auto will, wie finde ich raus ob wir überhaupt rote Autos haben, und wenn ja, ob eins davon frei ist?
Dummerweise habe ich nicht daran gedacht der Klasse eine Funktion mitzugeben, die nach Farben sortiert, also was müsste ich tun?

Ohne OOP baue ich einen Loop durchs globale array Fahrzeuge, bastel ein if() was Typ Auto, Farbe rot und Status frei aussortiert und gebe das Ergebnis aus.

Mit OOP muss ich zuerst mal rausfinden, in welcher Source-Datei die Klasse Fahrzeuge steht damit ich sie bearbeiten kann, welche Namen die abgeleiteten Klassen (Auto und LKW) haben, dann müsste ich rausfinden, welchen Namen die Klasse hat, in der das array Autos gespeichert ist und welche Funktion darin ich nutzen könnte um einen Loop durchs array zu bauen, dann müsste ich in die Klasse Farhzeuge eine neue Funktion einbauen, die diesen Loop laufen lässt und ganz abstrakt die gewünschten Kriterien (rot und frei) aussortiert, und schliesslich müsste ich noch in der Klasse Ausgabe die Sortierung nach Farben einbauen.

Ganz ehrlich, da verliere ich schon bei der Suche nach der richtigen Source-Datei die Übersicht, nicht zu reden von den nötigen zusätzlichen Deklarationen der neuen Funktionen.

Bevor aber jetzt die übliche Antwort kommt, ala "wenns ohne OOP so viel einfacher ist, dann mach doch ohne" (die Antwort kann ich in allen Variationen auswendig), bitte bitte, ich würde es unglaublich gerne lernen und ich weiss, dass ich mich hier richtig blöd anstelle.

Wenn ich ohne OOP 8 Loops ineinander verschachtele, dann habe ich keinerlei Probleme mit der Übersicht, aber wenn ich eine Struktur von Layern von Klassen bauen soll, wo ich mit einem einzigen Funktionsaufruf ganz oben etwas in Gang setzen soll, was im 8ten Layer von Subclass irgendwas ausführen soll, und dabei noch sicherstellen soll, dass die Funktion im 8ten Layer via Vererbung überhaupt weiss, mit welchen Werten sie rechnen soll, dann weiss ich schon im voraus, dass ich darüber niemals die Übersicht behalten kann.

C++ und OOP wurde doch entwickelt um es einfacher zu machen, warum erscheint es mir in den allerersten Schritten schon so viel schwieriger als alles was ich ohne OOP je gemacht habe?
 
Hm... du machst es mir nicht leicht ;)

Ohne OOP baue ich einen Loop durchs globale array Fahrzeuge, bastel ein if() was Typ Auto, Farbe rot und Status frei aussortiert und gebe das Ergebnis aus.
Falsch. Ohne OOP hast du kein Fahrzeug. Das Objekt, das du hast, von dem du die Farbe ermitteln kannst, ist die Instanz einer Klasse.


Mit OOP muss ich zuerst mal rausfinden, in welcher Source-Datei die Klasse Fahrzeuge steht damit ich sie bearbeiten kann, welche Namen die abgeleiteten Klassen (Auto und LKW) haben, dann müsste ich rausfinden, welchen Namen die Klasse hat, in der das array Autos gespeichert ist und welche Funktion darin ich nutzen könnte um einen Loop durchs array zu bauen, dann müsste ich in die Klasse Farhzeuge eine neue Funktion einbauen, die diesen Loop laufen lässt und ganz abstrakt die gewünschten Kriterien (rot und frei) aussortiert, und schliesslich müsste ich noch in der Klasse Ausgabe die Sortierung nach Farben einbauen.
Nein. 1.) Kannst du auch alles in eine .cpp packen, was bei großen Projekten schlichtweg völlig unübersichtlich und unpraktikabel ist. 2.) Es wird in keiner Klasse ein Array Autos gespeichert, das hat nichts miteinander zu tun. Du kannst Instanzen dieser Klasse (also Objekte) in einem Array oder Container speichern. 3.) Es ist mit OOP doch viel einfacher, dieses Farben-Problem nachzurüsten. Du hast einfach einen neuen std::string Farbe; in den private Bereich und machst dazu eine Schnittstelle, wie z. B. std::string getFarbe(); machst du das in der Klasse Fahrzeuge und erben PKW und LKW davon, so können diese nun alle ihre Farbe via getFarbe(); preisgeben, viel einfacher und effektiver.

Du musst weg von deinem merkwürdigem Array/Schleifen-Denken, das hat damit nicht zu tun.
 
Jau, ich bin immer der, ders selbst dann nicht kapiert, wenn mein Hund es schon bellen kann. ;)

Einen Anhaltspunkt hast du mir gerade gegeben: "weg von deinem merkwürdigem Array/Schleifen-Denken", das scheint der Knackpunkt zu sein, denn genau das mache ich tatsächlich, bzw. ich bins ja nicht anders gewöhnt.
Ohne OOP stehen Daten in arrays und werden mit Schleifen abgefragt, sortiert, usw.
Wenn mein Programm einen Satz Daten braucht, die in einer Datenbank gespeichert sind, dann liest das Programm zu Beginn die Datenbank, und packt das Ganze in ein array, so kenne ich es aus PHP und so habe ich es bis jetzt auch in C immer gemacht.

Wenn ich jetzt nur wüsste, was der Unterschied ist, zwischen "Instanz einer Klasse" und "member eines array", bzw. wie ich davon weg komme und was ich statt dessen denken soll.
Das sind für mich 2 verschiedene Wörter für das selbe Ding, so wie Auto und Kraftfahrzeug, Laptop und Notebook, wenn man es ganz genau definiert findet man Unterschiede, aber im Grunde ist es das Selbe.

Ich habe tatsächlich schon mal genau das versucht, "Thinking in C++", habe ich mir runtergeladen und ein Stück weit angeschaut.
Der Titel verspricht ja genau das, was ich brauche, aber nach 3 Tagen habe ich es völlig entnervt aufgegeben, genau wie alle anderen Tutorials und Bücher werden da seitenweise Sachen breit getreten, die ich auswendig kann, aber wenn etwas kommt, was ich nicht weiss und auch nicht auf Anhieb verstehe, ist die Erklärung so kurz, dass ich selbst nach 10 Mal nochmal lesen nur Bahnhof verstehe.

Seis drum, ich schätze dies wird sicher nicht mein letzter Versuch es zu lernen, und sofern ich lange genug lebe werde ich es irgendwann auch begreifen.
Ich habe inzwischen noch ein wenig weiter zum Thema im Web gestöbert, ein paar Seiten gefunden die ich noch nicht kannte, da muss ich mich erstmal durchlesen.

Fürs Erste sage ich danke für deine Mühe, zumindest habe ich jetzt ein wenig neue Motivation.
 
Zurück