Ab wann setzt ihr eigene Klassen ein?



  • Hallo, ich bin neu in C++ und komme von der C-Schiene. Ich habe jetzt ein paar Programme in C++ geschrieben(Tetris-Spiel, Adressverwaltung, kleiner Webgrabber) und noch nie das Bedürfnis gehabt, eine eigene Klassen für irgendeine Komponente dieser Programme zu entwickeln. Das einzige was ich mache ist halt Klassen zu verwenden die von Frameworks vorgegeben werden. Aber für eine eigene Klasse habe ich noch keinen Bedarf gehabt.

    Daher meine Frage, wann verwendet ihr Klassen?


  • Mod

    wann verwendet ihr Klassen?

    Die Frage ist so verdammt abstrakt, da wurden viele Bücher drüber geschrieben. Such dir mal eines über OOP.



  • Aber structs verwendest du!?


  • Mod

    Klassenmachen schrieb:

    Daher meine Frage, wann verwendet ihr Klassen?

    Dann, wenn keine passende Modellierung des Problems durch Basisdatentypen mehr möglich ist*. Was ungefähr ab dann der Fall ist, wenn zwei oder mehr Daten logisch zusammen gehören, d.h. praktisch in jedem Programm, das über ein Minibeispiel hinaus geht. Das kennst du auch aus C oder hast du dort nie structs verwendet? Und Funktionen, die auf Instanzen dieser structs arbeiten? Eine Klasse mit Memberfunktionen ist da nicht großartig anders, bloß die Syntax ist schöner.

    *: Dabei nicht schummeln! Bloß weil man mit double pos_x, pos_y, pos_z; so etwas wie einen Positionsvektor machen kann, so ist dies doch nicht konsequent. Die Werte gehören logisch zusammen und werden nie getrennt. Die Modellierung sollte dies auch ausdrücken, so können dann viele Logikfehler durch den Compiler erkannt werden. Also konsequent sein und nicht aus Faulheit eine suboptimale Lösung nehmen.



  • Ja, und Funktionen die damit arbeiten. Aber das muss ich nicht gleich in eine Klasse packen und dann erst ein Objekt davon instanzieren. Jedenfalls hatte ich noch nicht da Bedürfnis dieses zu tun, da es Mehrarbeit bedeutet ohne mir einen Mehrwert zu bringen. Die Programme laufen wunderbar und erweitern konnte ich sie auch gut.


  • Mod

    Klassenmachen schrieb:

    Ja, und Funktionen die damit arbeiten. Aber das muss ich nicht gleich in eine Klasse packen und dann erst ein Objekt davon instanzieren. Jedenfalls hatte ich noch nicht da Bedürfnis dieses zu tun, da es Mehrarbeit bedeutet ohne mir einen Mehrwert zu bringen. Die Programme laufen wunderbar und erweitern konnte ich sie auch gut.

    Ich behaupte mal, dass du entweder nicht weißt, was du verpasst und/oder deine eigenen Erfahrungen in C++ missglückt sind, wenn du das Objektmodell in C++ für Mehraufwand gegenüber C hältst. Eine der Hauptmotivationen zur Entwicklung von C++ war, dass Objektorientierung in C dermaßen umständlich ist und so viel Disziplin vom Programmierer verlangt. Klassen in C++ haben eine viel knackigere Syntax als die struct/Funktion-Kombo in C und gleichzeitig ist bei einer (gut geschriebenen) C++-Klasse die korrekte Benutzung ziemlich narrensicher, während bei C eiserne Disziplin und Aufmerksamkeit gefragt ist, wenn man komplexere Datentypen korrekt behandeln will.
    Typisches C++:

    string foo = "Beispiel";
    string bar = foo;
    
    bar = "Hallo Welt";
    foo = bar;
    
    cout << foo;
    

    Etwas Vergleichbares in üblichem objektorientiertem C:

    string_handler foo = create_string_from_char("Beispiel");
    string_handler bar = copy_create_string(foo);
    
    string_assign_from_char(bar, "Hallo Welt");
    string_assign_from_string(foo, bar);
    
    put_string(foo, stdout);
    
    delete_string(foo);  // In C nötig, in C++ nicht!
    delete_string(bar);
    


  • Klassenmachen schrieb:

    Ja, und Funktionen die damit arbeiten. Aber das muss ich nicht gleich in eine Klasse packen und dann erst ein Objekt davon instanzieren. Jedenfalls hatte ich noch nicht da Bedürfnis dieses zu tun, da es Mehrarbeit bedeutet ohne mir einen Mehrwert zu bringen. Die Programme laufen wunderbar und erweitern konnte ich sie auch gut.

    Nimm an, du hast ein Spielobjekt, z.B. ein Monster in einem 2D-Spiel. Dieses besitzt diverse Attribute wie Position, Blickrichtung oder Trefferpunkte. Naheliegend ist nun, diese Attribute zu einer Klasse zusammenzufassen, weil du dann mit Monstern arbeiten kannst statt einzelnen Daten. Das erlaubt Abstraktion, Kapselung, aussagekräftigen Code und vieles mehr.

    Wenn du nun 100 Monster hättest, wie würdest du das modellieren? Würdest du ein einzelnes Array für alle Positionen, eines für alle Blickrichtungen und eines für alle Trefferpunkte haben? Und wenn ein Monster stirbt, musst du aus jedem Array den Eintrag löschen und alles synchron halten?

    Vernünftig wäre hier, einen std::vector<Monster> zu nehmen und so alle Monster gemeinsam abzuspeichern. Dafür brauchst du aber einen Datentyp Monster -- eine Klasse.



  • Wenn ich hundert Monster habe, dann habe ich einen Speicherbereich mit hundert zeigen auf die Struktur Monster und halt die Funktionen die mit diese Struktur umgehen.

    Als String nutze ich bis jetzt nur die CStrings, bis dato reichten die mir immer.

    Aber im Prinzip weiß ich worauf ihr hinaus wollt, nur werden meine Ein-Mann-Mini-Projekte nie so groß, als dass mir die Arbeitsweise mit Strukturen und Funktionen irgendwelche Probleme bereiten würden.

    Hmm naja, danke erst einmal für die kompetenten Antworten. Ich muss weiter machen. Programme werden nicht fertig, wenn man sich in Foren aufhält 😉



  • Klassenmachen schrieb:

    Wenn ich hundert Monster habe, dann habe ich einen Speicherbereich mit hundert zeigen auf die Struktur Monster und halt die Funktionen die mit diese Struktur umgehen.

    Gleiches hast du mit Klassen. Nur hast du keine Speicherbereiche und manuelle Speicherverwaltung mehr, sondern Container und RAII. Du kannst dir massiv unnötige Arbeit und Fehler sparen, ausserdem wird Code lesbarer.

    Vergleiche

    struct Monster { ... };
    
    size_t numberOfMonsters = 100;
    size_t monsterCapacity = 100;
    Monster* monsters = malloc(monsterCapacity * sizeof(Monster));
    size_t i;
    
    for (i = 0; i < numberOfMonsters; ++i)
        monsters[i] = Monster_Create(); // setzt sinnvolle Initialwerte
    
    // lösche Monster
    memmove(monsters + indexToDelete, monsters + indexToDelete + 1, numberOfMonsters - indexToDelete - 1);
    --numberOfMonsters;
    
    // Aufräumen
    for (i = 0; i < numberOfMonsters; ++i)
        Monster_Destroy(&monsters[i]); // falls sie sich irgendwo abmelden müssen
    free(monsters);
    

    mit

    class Monster { ... };
    
    std::vector<Monster> monsters(100);
    // alle schon konstruiert und initialisiert
    
    monsters.erase(monsters.begin() + indexToDelete);
    // löscht Element, kopiert selbstständig um, passt size() automatisch an
    
    // gibt Speicher selbstständig frei
    

    Es ist absolut unvernünftig, bei C zu bleiben.



  • Das C++-Beispiel zeigt den Vorteil von Vektoren nicht von Klassen.



  • fsfassasgsa schrieb:

    Das C++-Beispiel zeigt den Vorteil von Vektoren nicht von Klassen.

    Doch, natürlich ➡ RAII



  • Klassenmachen schrieb:

    Daher meine Frage, wann verwendet ihr Klassen?

    Ich würde ganz naiv antworten: dann wenn ich es für angebracht halte.

    Und es ist IMHO angebracht, wenn ein Haufen Daten auf Grund der Modellierung 'untrennbar' zusammen gehören und es auch nur Sinn macht mit einer ganz bestimmten Auswahl von Funktionen auf diesen Daten zu arbeiten. Und last but not least wenn dieser Daten-Haufen-mit-Funktionen auch noch eine Individualität besitzt.

    Klassenmachen schrieb:

    Wenn ich hundert Monster habe, dann habe ich einen Speicherbereich mit hundert zeigen auf die Struktur Monster und halt die Funktionen die mit diese Struktur umgehen.

    dann betreibst Du bereits OOP, nur dass Du auf die Hilfsmittel von C++ verzichtest. Dazu gehören unter anderen Konstruktor und Destruktor.
    Wenn Du meinst, dass man darauf verzichten kann, so schreibe doch mal eine einfach verkettete Liste - einmal mit Klasse und einmal ohne.

    man muss natürlich aufpassen, was man tut. Ein Klassen-Design kann auch nach hinten losgehen; indem es nur mehr Code produziert und nichts bringt.
    Klassen-Design bringt immer genau dann etwas, wenn man zumindest einen Teil der internen Komplexität vor dem Anwender 'verstecken' kann. D.h. für den Anwender sollte die Anwendung der Klasse immer einfacher sein, als mit dem besagten Haufen von Daten und Funktionen zu arbeiten.

    Schau Dir doch mal diesen Code und diesen Code an - beide machen praktisch das selbe. Wäre auch nett, wenn Du das kommentieren würdest.
    Vielleicht findest Du den Code ohne Klassen viel einfacher, dann solltest Du in so einem Fall auch keine verwenden.

    Gruß
    Werner



  • Ja, dann mache ich anscheinend bereits schon OOP und dann wird auch schon immer sehr viel OOP in C gemacht. Ich finde es halt in C einfacher fremden Code zu lesen, als wenn ich mich durch ein paar Klassen kämpfen muss und nur erraten kann wozu sie eigentlich gut sind.

    STL ist sollte man nicht als Argument für Klassen sehen, da so etwas auch nur externe Funktionalität ist. Um dann zu vergleichen, müsste man schon in C das Beispiel dann mit der GLib oder zu aufzeigen.

    Wenn ich die Fragen hier sehe mit Temp-Objekten, Const-Correctnes und die hundert Überladungen und Konstruktoren wird mir eher schlecht, als dass ich mich auf die Features freuen würde. C++ Code wird auch schnell kryptisch und ich behaupte mal dass man C-Programme in der Regel immer schneller verstehen kann wie irgendwelche C++-Konstrukte über mehrere Klassen verteilt und dann vielleicht noch mit Templates STL und alles bunt gemischt. Da blickt doch kaum ein Außenstehender ohne ordentliche Doku mehr durch und eine ordentliche Doku ist der absolute Ausnahmefall.

    Na, aber wie hier schon geschrieben wurde, sollte man Klassen einsetzen wenn sie erforderlich sind und einen Vorteil bringen. Bei mir waren sie in den letzten Jahren noch nie erforderlich, aber das kann sich ja noch ändern.



  • Das ligt daran, dass du an C gewöhnt bist, an C++ aber nicht. Ich finde C total unübersichtlich und kryptisch weil ich mir zu jeder Datenstruktur raus suchen muss, welche Funktionen nun dazu gehören.



  • Ja, da mag wohl viel Macht der Gewohnheit dabei sein. Ich mag halt einfache Sprachen. Aber ich betreibe ja C auch nur als Hobby und bin von Beruf Handwerker. Programmieren dient mir halt zur Entspannung nach der anstrengenden Arbeit.

    Ich werde mich mal demnächst mit den Klassen beschäftigen, mir war halt nicht klar warum ich sie nutzen sollte, weil ich bis jetzt wunderbar ohne sie ausgekommen bin.



  • Klassenmachen schrieb:

    STL ist sollte man nicht als Argument für Klassen sehen, da so etwas auch nur externe Funktionalität ist.

    Wenn Du der Meinung bist, dann solltest du bei C bleiben.
    Weil dann wirst Du über das Stadium C++ sei C mit Klassen und cin/cout nie hinauskommen.
    Die STL gehört zu C++ und ist KEINE externe Bibliothek 😮



  • Wenn ich die Fragen hier sehe mit Temp-Objekten, Const-Correctnes und die hundert Überladungen und Konstruktoren wird mir eher schlecht, als dass ich mich auf die Features freuen würde.

    const-correctness hat nichts mit C++ zu tun, die ist in C mindestens genauso wichtig. Klassen bieten dir vor allem 3 Vorteile (und noch mehr, aber ich denke das hier sollte überzeugend genug sein):

    1. Kapselung. Du kannst Member private machen, was bedeutet du kannst die Zahl der Funktionen die auf diese Member Zugriff haben deutlich einschränken. (Von alle auf z.B. 5) Das verringert die Zahl der Stellen an denen man nach Fehlern in einer Klasse suchen muss deutlich. Nehmen wir nur mal als Beispiel einen vector in C:

    int_vector v = create_new_int_vector(50);
    v.size = 55; // oups, Objekt broken
    

    Ist das ein Programmierfehler? Klar! Aber die versuchen wir ja gerade einzuschränken. In C++ ist dieser Fehler von außen schlicht nicht möglich, weil der Member "size" private wäre.*

    2. RAII. Durch Konstruktoren und Destruktoren können Objekte ihre Ressourcen selbst verwalten. Typisches Beispiel für ein C-File-Wrapper:

    class cfile
    {
    	FILE* handle_;
    
    public:
    	cfile(const char* filename, const char* mode)
    		: handle_(fopen(filename, mode))
    	{
    		if (!handle_)
    		{
    			// throw something
    		}
    	}
    
    	cfile(cfile&& other)
    		: handle_(other.handle_)
    	{
    		other.handle_ = nullptr;
    	}
    
    	~cfile()
    	{
    		if (handle_ != nullptr)
    			fclose(handle_);
    	}
    
    	// Methoden für read/write
    };
    
    int main()
    {
    	cfile file("test.txt", "rt"); // Ein file ist immer in einem gültigen Zustand
    	file.read(...);
    
    	std::vector<cfile> v;
    	v.push_back(std::move(file)); // Und kann problemlos in Standardcontainer integriert werden.
    	// Das file wird automatisch geschlossen 
    	// sobald es aus dem Container entfernt, 
    	// oder der Destruktor des Containers aufgerufen wird.
    }
    

    Und hier sieht man auch einen wie ich finde deutlich größeren Vorteil von RAII als dass man das eine fclose am Ende des Scopes nicht vergessen kann, dann die meisten erfahrenen C-Programmierer werden sowas wohl im Blut haben. Aber dass die Besitzverhältnisse mit Containern und letztlich im ganzen Programm über konsistent sind, das schafft man mit C einfach nicht. Dort musst du immer überlegen an welcher Stelle Ressourcen jetzt wieder frei gegeben werden. (Falls du es überhaupt schaffst dir einen generischen Container über Makros hin zu frickeln, wird dieser sich sicherlich nicht um die Freigabe der Ressourcen seiner Objekte kümmern können, das musst du wieder manuell machen.) Zudem musst du bei Funktionen die einen Pointer oder Ähnliches in C zurückgeben auch jedes mal nachgucken, wie du den Kram denn wieder frei gibst (falls du es nicht gleich vergisst und ein Leck produzierst ;)), in C++ kann man einfach das obige Pattern anwenden und alles gibt sich selbst frei, garantiert. (Spätestens wenn du Exceptions nutzen willst geht ohne RAII gar nichts mehr, aber das ist ein anderes Thema.)

    3. Vererbung / virtual. Das ist wohl der uninteressanteste Vorteil, aber sollte man mal erwähnen. Das gibt dir im Prinzip Funktionspointer in structs die automatisch auf die richtige Funktion zeigen. Aber dafür gibt's genug Beispiele im Netz.

    Für Vorteile von C++ im Allgemeinen sollte man wohl auch noch Templates, Operatorüberladungen, type-safety an kleinen Stellen, Lambdas, die umfangreichere und besser designte Standardlibrary, Exceptions, ... erwähnen, aber hier sollte es ja nur um Klassen gehen.

    * Ja, man könnte natürlich einen Pointer nehmen, den casten und darüber das Feld verändern. Private ist eine reine Einschränkung beim Kompilieren, der Speicher wird nicht magisch geschützt. Aber es geht hier darum Flüchtigkeitsfehler und falsche Anwendung zu vermeiden, wer unbedingt auf einen privaten Member zugreifen will kann auch einfach das ein public: vor setzen.

    STL ist sollte man nicht als Argument für Klassen sehen, da so etwas auch nur externe Funktionalität ist.

    Da kann ich nur teilweise zustimmen - viele Konzepte der STL sind ja erst durch verschiedene Sprachfeatures möglich (Klassen/Templates). z.B. eben dass der vector seinen Kram selbst aufräumt wenn er out-of-scope geht.



  • MichelRT schrieb:

    Klassenmachen schrieb:

    STL ist sollte man nicht als Argument für Klassen sehen, da so etwas auch nur externe Funktionalität ist.

    Wenn Du der Meinung bist, dann solltest du bei C bleiben.
    Weil dann wirst Du über das Stadium C++ sei C mit Klassen und cin/cout nie hinauskommen.
    Die STL gehört zu C++ und ist KEINE externe Bibliothek 😮

    Das ist mit als Programmierer doch völlig egal ob das nun eine externe Lib ist, oder zum Standard gehört. Ohne externe Libs kann man eh kaum arbeiten.

    Wozu muss ich über einen bestimmtes Studium herauskommen? Es funktioniert doch alles wie es soll. Ich habe ein Problem, male mir ein paar Sachen dazu auf und suche Algos die mir helfen könnten. Dann entwickle ich die ersten Prototypen, sehe dabei wo größere Probleme auftreten können und das wird dann soweit verfeinert, bis ich keine Fehler mehr drin habe und meine Aufgabe erfüllt ist.

    Dies alles funktioniert bei mir seit Jahren ohne eine einzige Klasse haben zu müssen und ich wollte in Erfahrung bringen, warum anderen es anscheinend nicht reicht und diese Leute Klassen unbedingt brauchen.

    Ich denke, das wurde hier nun genug da gelegt. Alle angesprochen Probleme habe ich in meinen Programmen nie wirklich als ernsthaftes Problem angesehen. Wie gesagt, es funktioniert alles wie es soll und auch nach Jahren komme ich noch gut mit meinem Code von damals zurecht.

    Klassen bleiben für mich wohl daher ein ziemlich sinnloses Konstrukt. Aber solange alles klappt, bleibe ich bei dem Motto: "Never change a runnning system"

    Soll ich irgendwann mal Riesenprojekte mit mehreren Leute angehen, dann mögen Klassen durchaus Vorteile haben. Aber bei meinen Ein-Mann-Projekten, sind sie nur ein unnötiger Mehraufwand.



  • Ja ist ja gut jetze, wir habens verstanden: Du findest Klasse(n) scheiße.
    Mit der selben Argumentation kann man auch C vergessen und bei Assembler bleiben, und überhaupt wozu brauche ein Betriebssystem?

    Das hat sich nunmal entwickelt, weil die Leute (endlich) anfingen Konzepte zu entwickeln und daraus zu abstrahieren. Das ist fast in jeder technischen, wissenschaftlichen Disziplin so. Klassen sind ein Abstraktionskonzept. Natürlich braucht man das nicht, aber es macht vieles eifnacher (sonst hätte man es garnicht für notwendig empfunden sowas zu entwickeln).

    Ein Beispiel:
    Es muss doch jedem klar sein, dass wenn ich z.B zwei (mathematische) Vektoren addiere, ich nicht jedesmal darauf hinweisen will das man dazu alle Komponenten addiert. Das nervt auf Dauer. Genau das kann man in C++ mit Klassen nachbilden.
    Natürlich kann ich alle Komponenten getrennt behandeln, sie gehören aber logisch zusammen.



  • Klassenmachen schrieb:

    Aber bei meinen Ein-Mann-Projekten, sind sie nur ein unnötiger Mehraufwand.

    Nein, sind sie nicht.


Anmelden zum Antworten