object oriented?

Thomasio

Erfahrenes Mitglied
Nachdem ich das Kapitel Konvertierungen erfolgreich durch habe, versuche ich mich gerade mit ersten Schritten in Objekt orientiertem programmieren, aber irgendwie begreife ich die Sache nicht

Bei global, static, public, private usw kome ich ja noch mit

Code:
int a = 1;   // global variable, sichtbar im ganzen file

void Subprogramm();    // Prototyp;

int main()
{
int b = 1;   // lokale Variable, nur sichtbar innerhalb main()
static int c = 1;    // lokale Variable, aber durch "static" im Prinzip auch global
Subprogramm();
}

void Subprogramm()
{
a++;    // kein Problem weil global
int b = 2;   // neue lokale Variable, nur sichtbar im SubProgramm unabhängig von main()
}

aber wenn ich auch nur ansatzweise versuche mein Programm über mehrere Dateien zu verteilen, dann geht nichts mehr, wenn ich also z.B. void irgendwas() in ein separates file packe und die beiden im compiler verlinke, sind selbst globals nicht mehr sichtbar
Mir ist sogar klar warum, global heisst halt filescope und nicht systemscope
Mir ist auch klar, dass ich globals so weit wie möglich vermeiden sollte, aber ich würde wenigstens gern verstehen wie es funktioniert, mit "soll man nicht tun, also muss man auch nicht wissen wie" gebe ich mich einfach nicht zufrieden

Nachdem ich aber scheinbar selbst für Anfänger Tutorials zu blöd bin, wäre ich dankbar, wenn mir das mal einer für ganz doofe erklären könnte

1) Wenn Subprogramm() in einem separaten file liegt, warum kann ich es dann aus main() heraus aufrufen?
2) Wie mache ich Variablen aus einem file in einem anderen file sichtbar?

Ich habe halt die Vorstellung, dass wenn ich irgendwann mal was grösseres schreibe, es nahezu automatisch über mehrere files geht, und ich wüsste doch zu gerne, wie das funktioniert
Oder liege ich evntuell so völlig daneben, dass das hier alles überhaupt keinen Sinn macht?
 
Erstmal sei gesagt, dass OOP nichts mit dem Verteilen vom Code in verschiedene Datein zu tun hat. OOP hat grob gesagt etwas mit Klassen zu tun. Du meinst bestimmt Modulare Programmierung.

1) Wenn Subprogramm() in einem separaten file liegt, warum kann ich es dann aus main() heraus aufrufen?

Weil die Funktion Global Scope hat.

2) Wie mache ich Variablen aus einem file in einem anderen file sichtbar?

Beispiel:

extern int b
 
Irgendwo habe ich gelesen, was OOP ist weiss keiner so genau, ist eher eine Philosophie als feststehende Technik
Für mich (als totaler Anfänger) bedeutet OOP erstmal, dass ich in meinem Programm Objekte habe, deren Verhalten durch Funktionen (oder Klassen) beeinflusst wird, so dass ich an jedem Objekt einzeln programmieren kann, ohne die anderen Objekte zu stören
Wenn ich also z.B. einen Taschenrechner programmieren will, dann ist Multiplikation eine andere Funktion als Addition, die beide die gleichen Variablen verwenden können, ohne sich gegenseitig zu stören

Was mir nicht in den Kopf will ist, dass es im Beispiel des Taschenrechners mindestens eine globale Variable geben muss, auf die alle Funktionen Zugriff haben, nämlich der aktuelle Wert der Anzeige
Ich kann noch nicht mal einen Beispielcobe anbieten, weil ich mich spätestens in Zeile 3 verlaufe, ich habe nicht mal ganz grob eine Vorstellung davon, wie ich so etwas anders als mit meinen aus php gewohnten Spaghettistil machen soll

Mal angenommen das Programm läuft etwa so:

Main Programm mit Anzeige des Taschenrechners, allen Tasten und dem Display, wo der aktuelle Wert steht

Programmstart: Warten auf Eingabe

User Eingabe: 1

Programm: Funktionsaufruf Eingabe(), speichern der Eingabe in einer Variablen, sagen wir mal int a; und Ausgabe im Display, Rückkehr zu warten auf Eingabe

User Eingabe: +

Programm: Funktionsaufruf Eingabe(), speichern der Eingabe um später zu wissen, welche Funktion aufgerufen werden muss, keine Änderung im Display, Rückkehr zu warten auf Eingabe
Hier wirds aber schon kompliziert, weil ich zuerst mal rausfinden muss, ob der User eine Zahl oder eine Rechenfunktion eingegeben hat, aber das schaffe ich grad noch

User Eingabe: 2

Programm: Speichern der Eingabe in einer Variablen, sagen wir mal int b; und Ausgabe im Display, Rückkehr zu warten auf Eingabe
Hier stehe ich dann im Wald, woher soll mein Programm wissen, ob die Eingabe eine zweite Zahl von a, oder eine neue Zahl also int b sein soll? Das Einzige was mir dazu einfällt wäre, eine global zu verwenden, in der der letzte eingegebene Wert gespeichert wird, und wenn das eine Rechenfunktion war, dann muss die Eingabe eine neue Zahl sein, aber so ein Umstand kann sicher nicht der Sinn der Sache sein

User Eingabe: =

Programm: Funktionsaufruf Addition(a, b), Speichern und Ausgabe des Rückgabewertes im Display, Rückkehr zu warten auf Eingabe

User Eingabe: *

Programm: Speichern der Eingabe um später zu wissen, welche Funktion aufgerufen werden muss, keine Änderung im Display, Rückkehr zu warten auf Eingabe

User Eingabe: 3

Programm: Hier verlaufe ich mich dann völlig, woher soll mein Programm wissen, ob dies nun eine zweite Zahl von a, ein neues a, eine zweite Zahl von b oder ein neues b sein soll?

User Eingabe: =

Programm: Nix mehr, da bin ich mit meinem Latein am Ende, Funktionsaufruf Multiplikation(), schön, aber woher die Variablen nehmen die da übergeben werden sollen?

Und wenn ich dann versuche die einzelnen Funktionen des Taschenrechners auch noch in jeweils andere files zu packen setzt es bei mir total aus

extern a; // prima, wenn a mehrfach in diversen Funktionen oder files existiert, welches a nimmer er denn dann, von wo und warum?
 
um es nochmal deutlich zu machen, mir ist völlig klar, wie ich sowas im Spaghettistil machen kann, mit lauter globals, nur die Umsetzung auf OOP bringe ich nicht mal ansatzweise zustande
 
das ist erstmal die Klasse:
Code:
class taschenrechner
{    
    private:
      double zahl1,zahl2;
      char rechenzeichen[2];
    
    public:
       set_zahl1(double para);
       set_zahl2(double para);
       set_operation(char operation[2]);
       rechnen();
       get_ergebnis();
}
jetzt folgen die Methoden der Klasse:
Code:
taschenrechner::set_zahl1(double para)
{
    zahl1 = (double) para;
}
taschenrechner::set_zahl2(double para)
{
    zahl2 = (double) para;
}
taschenrechner::set_operation(char operation[2])
{
    switch(operation)
    {
        case "+" :
        case "-" :
        case "/" :
        case "*" :
             strcpy(rechenzeichen,operation);
             break;
        default:
             return 0;
    }
}
taschenrechner::rechnen()
{
    switch(rechenzeichen)
        {
            case "+" : ergebnis =  zahl1 + zahl2;break;
            case "-" : ergebnis =  zahl1 - zahl2;break;
            case "/" : ergebnis =  zahl1 / zahl2;break;
            case "*" : ergebnis =  zahl1 * zahl2;break;
        }
}
taschenrechner::get_ergebnis()
{
    return ergebnis;
}
insgesamt siehts so aus:
Code:
class taschenrechner
{    
    private:
      double zahl1,zahl2;
      char rechenzeichen[2];
      double ergebnis;
    
    public:
       set_zahl1(double para);
       set_zahl2(double para);
       set_operation(char operation[2]);
       rechnen();
       get_ergebnis();
}

taschenrechner::set_zahl1(double para)
{
    zahl1 = (double) para;
}
taschenrechner::set_zahl2(double para)
{
    zahl2 = (double) para;
}
taschenrechner::set_operation(char operation[2])
{
    switch(operation)
    {
        case "+" :
        case "-" :
        case "/" :
        case "*" :
             strcpy(rechenzeichen,operation);
             break;
        default:
             return 0;
    }
}
taschenrechner::rechnen()
{
    switch(rechenzeichen)
        {
            case "+" : ergebnis =  zahl1 + zahl2;break;
            case "-" : ergebnis =  zahl1 - zahl2;break;
            case "/" : ergebnis =  zahl1 / zahl2;break;
            case "*" : ergebnis =  zahl1 * zahl2;break;
        }
}
taschenrechner::get_ergebnis()
{
    return ergebnis;
}

Um die Klasse anzuwenden, machst du folgendes:
Code:
#include <iostream>

taschenrechner casia;
casia.set_zahl1(5.8);
casia.set_zahl2(7.1);
casia.set_operation('+');
casia.rechnen();
std::cout << casia.get_ergenis()  << endl;

verzeiht mir bitte die Formatierung im Code.
 
hübsch, das sieht etwa genauso aus wie ich es in diversen Tutorials finde, ich verstehe zwar, was da wie und warum passiert, aber ich kapiere es nicht wirklich, sprich ich könnte den code nehmen und in meinen Compiler pasten, das funktioniert dann, aber wenn ich versuche es so weit zu verstehen, dass ich Änderungen oder Erweiterungen vornehmen kann stehe ich sofort mitten im Wald

z.B.:
wenn ich set_zahl aus nem std::cin holen will, weil ja ein Taschenrechner normalerweise nicht mit vorgegebenen Werten rechnet, dann schaffe ich das so grad noch, aber wenn ich dem User die Möglichkeit geben will (wie bei allen herkömmlichen Taschenrechnern üblich), dass er Zahlen einzeln eingibt, also z.B. 10 als 1 (ENTER) 0 (ENTER), dann fehlt meiner class die Unterscheidung, ob er nun die 1 und die 0 als 2 chars der gleichen Zahl nehmen oder damit rechnen soll und allerspätestens wenn der User etwas in dieser Art eingibt:

1 (ENTER) + (ENTER) 2 (ENTER) = (ENTER) * (ENTER) 3 (ENTER) = (ENTER)

dann ist Essig mit der class, weil er bei der Eingabe von * 3 = nicht mehr weiss ob die 3 nun zahl1 oder zahl2 ist, und selbst wenn er das raus bekommt, dann weiss er immer noch nicht woher er zahl1 nehmen soll, sofern zahl1 und/oder ergebnis keine globals sind
 
Du möchtest einen Taschenrechner programmieren, bei den man die gesamte Aufgabe so eingibt:

(1*4-6)*7 und das Ergebnis erhält?

Schau mal unter Kellerautomat. Du solltet ein Array für die Eingabe verwenden.
 
Nein, ich möchte überhaupt keinen Taschenrechner programmieren, ich möchte lernen, wie man aus Spaghetticode OOP code macht, der Taschenrechner war dabei nur ein Beispiel, anhand dem ich versucht habe zu erklären, warum ich nicht mal die Grundgedanken zur OOP verstehe
Die Antwort mit dem fertigen Code für den Taschenrechner spiegelt dabei ziemlich genau mein Problem
Solange ich ganz exakt vorgegebene Daten habe, kann ich wunderhübsch eine class dazu schreiben und die Daten verarbeiten, ganz prima, nur hat man halt sehr selten exakt vorgegebene Daten, eine Variable ist nun mal variabel und wird in der Praxis nahezu niemals in Form von

casia.set_zahl1(5.8);

gesetzt, sondern immer durch Usereingabe oder Abfrage anderweitig bereits existierenden Variablen
Also habe ich 2 mögliche Probleme:
1) Die Usereingabe kann ungültig sein, bzw nicht so, wie meine class das gerne hätte, z.B. wenn der User einen Buchstaben statt einer Zahl eingibt, steht meine class auf dem Schlauch und liefert vermutlich nur Unsinn als Ergebnis, sprich ich muss erstmal prüfen, ob der User überhaupt einen gültigen Wert eingegeben hat
2) Wenn die class eine anderweitig bereits existierende Variable verarbeiten soll, dann habe ich mit OOP grundsätzlich das Problem, dass diese Variable für meine class gar nicht erst sichtbar ist, zumindest verstehe ich nicht, wie ich innerhalb der class an den Wert der Variablen herankommen soll

Das könnte ich jetzt noch 3 Seiten lang ausschmücken, aber ich denke es ist schon klar, wo mein Problem liegt
Ich bin es einfach von php her gewohnt, dass alle Variablen immer global sind, sprich ganz egal, wo und wie eine Variable gesetzt wird, sämtliche Funktionen haben ohne weiteres Zugriff auf deren Wert
Mit OOP lande ich beim kleinsten Versuch irgendetwas auszuprobieren sofort bei dem Problem, dass innerhalb einer class sämtliche Variablen aus allen anderen Funktionen unsichtbar sind und ausser dass ich die alle von Anfang an global mache sehe ich keinen Weg, wie ich überhaupt eine class erstellen kann, die irgendwas berechnen könnte, ganz egal was die class tun soll
Abgesehen von diesen Beispielen, die ich auch in Tutorials finde, macht der ganze OOP Kram keinen Sinn für mich Anfänger, in der Praxis kann man nun mal nicht eine passende Variable direkt vor Aufruf der class definieren und ihr auch noch gleich einen zur class passenden Wert geben, auch hat man in der Praxis nicht genau EINEN Durchlauf der class und dann gleich Programmende, sondern man muss zuerst den Wert der Variablen von woanders her holen, die class damit durchlaufen lassen, den Rückgabewert speichern und im Programm WEITER machen statt Ende, was aber dank OPP und unsichtbaren Variablen einen Umstand bedeutet, den ich gar nicht erst verstehe, sprich ich weiss nicht mal, WIE ich denn den Wert einer Variablen sichtbar machen soll, WIE ich den Rückgabewert speichern soll, wenn der Ort wo auch immer ich den Rückgabewert speichere für sämtliche andere Funktionen gar nicht erst sichtbar ist und WIE ich überhaupt ein Programm schreiben soll, in dem sämtliche Variablen grundsätzlich unsichtbar sind
 
Hi,

ich weiß nicht ob ich dein Problem richtig verstanden habe, aber ich versuchs mal zu erklären.

Zuerst muss gesagt sein OOP macht nicht immer Sinn.
Mit der Zeit bekommt man Erfahrung darin und sieht sofort, wo sich ein Objekt anbietet.
Das ist zB. der Fall, wenn bestimmte Abläufe oft gemacht werden müssen und dabei jedesmal andere Werte genutzt werden.
Die Werte die dabei benutzt werden muss man der Klasse irgendwie mitteilen. Das macht man entweder im Konstruktor, mit einem Funktionsaufruf, mit einem Parameter der Funktion oder die Klasse holt ihn sich von selbst aus einer anderen Quelle (was nur selten nötig ist).
Die werte werden dann, falls sie mehrmals gebraucht werden, zwischengespeichert.
Wichtig ist, dabei (sogut wie) keine globalen Variablen genutzt werden. Es ist auch nicht nötig.

Zu dem 1. Punkt von dir:
Ob die Eingabe gültig ist kann man innerhalb der Klasse prüfen. In dem Beispiel würde ich sie aber vor dem übergeben an die Klasse (per set_zahl1) usw. prüfen.

Zu 2. :
Wie gesagt. Die Werte die die Klasse braucht, werden ihr irgendwie vorher mitgeteilt.

Richtig interessant wird die OOP, wenns um Vererbung geht.
Man kann sich viel arbeit sparen und den Code sehr übersichtlich machen, indem man Basisklassen schreibt und diese dann Vererbt.

Ich kann jetzt nur noch n Beispiel bringen. Da ich OOP meistens beim Spiele proggen benutze, bringe ich mal n Beispiel von da. (auch wenns nich so toll is, aber mir fällt grad nix besseres ein xD)

Also du willst einen Ball zeichnen.
Ohne OOP würde man das so machen
C++:
struct ball
{
  int x, y, farbe;
};

void draw(ball* pball)
{
  // Zeichnet den Ball anhand der angaben in der Struktur
}

// in der main oder sonstwo
ball b;

b.x = x;
b.y = y;
b.farbe = farbe;

draw(&b);

Naja das ist nicht gerade schön.

in OOP siehts so aus
(ich pack ma der kürze halber alles in eines)
C++:
class ball
{
  public:
    ball(int x, int y, int farbe) : m_x(x), m_y(y), m_farbe(farbe)   // das ist eine Möglichkeit die Werte der Member zu setzen
     {  }

     void draw()
      {
         // zeichnen
       }
   private:
     int m_x, m_y, m_farbe;
};


// irgendwo im nirgendwo
// will man jetzt einen Ball zeichnen macht man einfach

ball b(x, y, farbe);
b.draw();

Und das kann man so oft machen wie man will.
 
Vielleicht wird es so deutlicher:

Wenn ich eine Webseite in php schreibe, ist eine der wichtigsten Funktionen immer die memberlist
Die muss für nahzu alle ihre Funktionen zu Beginn geladen, dann bearbeitet und am Ende wieder gespeichert werden
Sagen wir mal ich mache das nicht gleich mit einer tollen Datenbank, sondern einfach mit einem txt file

Beginn, Daten einlesen:

In php ganz einfach fopen und zeilenweise in ein array einlesen
In C++ brauche ich dafür schon ein mehrdimensionales array, weil ein string bereits ein array ist, aber wenigstens machbar
Mit OOP ist schon beim einlesen Schluss, weil innerhalb einer class, in welcher Form auch immer ich die eingelesenen Daten speichern will, diese am Ende der Class nicht mehr existieren, sofern ich nicht im voraus ein globales mehrdimensionales array definiert habe

Bearbeitung, Daten ändern:

In php wieder ganz einfach, im array den zu ändernden Datensatz suchen, ändern, fertig
Mit OOP hätte ich nicht mal einen Ansatz zur Lösung, weil ohne globales array die Daten gar nicht erst existieren bzw. unsichtbar sind und zwar bereits in der Funktion die die class aufrufen soll, sprich ich kann der class gar nicht erst irgendetwas zur Berechnung übergeben

Bearbeiten, vergleichen vorher nachher:

In php einfach die alte Liste nochmal einlesen, mit 3 kurzen Schleifen existierende Elemente gegenseitig vergleichen, Ergebnisse in neue arrays, liefert mir sowohl entfernte als auch neue und geänderte Einträge
In C++ grenzt das schon an Chaos, mit 5 mehrdimensionalen arrays, jeden Datensatz zu <string> konvertieren, weil man damit besser arbeiten kann usw, aber mit etwas Übung schafft man das schon
Mit OOP fange ich da gar nicht erst an, die class, die mir das einliest hat schon wieder keinen Ort wo das Ergebnis gespeichert werden könnte, die class die vergleichen soll hat keinerlei Daten zum vergleichen, weil alles unsichtbar ist, von Rückgabewerten die in 3 neue arrays sollen mal gar nicht zu reden

Ende, Daten speichern:

In php, fopen, schleife durchs array und fwrite, fertig
In C++ fange ich erstmal das grosse Konvertieren an, erstmal schauen, ob das Ganze seit dem Einlesen bearbeitet wurde und zu strings geworden ist, alles zurück konvertieren zu char* usw, aber irgendwie schaffe ich das dann schon
Mit OOP geht weder noch, ich habe keine sichtbaren Daten, ich kann nicht mal feststellen ob überhaupt irgendwas bearbeitet wurde, konvertieren unsichtbarer Daten ist auch nicht ganz so einfach, und speichern unsichtbarer Daten habe ich bis jetzt auch noch nicht geschafft
 
Zurück