Objektorientierte Programmierung, Ausgabe ist nicht korrekt.


#1
Hallo zusammen,
ich habe ein kleines Problem. Und zwar habe ich eine recht einfache Aufgabe gestellt bekommen, aber leider finde ich nicht den Fehler in meinem Quellcode, der Compiler findet keinen Fehler, aber das Ergebnis ist total falsch. Ich hoffe das mir jemand helfen kann und mir sagen kann wo der Fehler liegt.
1eb2b6665e4e00b156e28bd16a8f25d3.png

C++:
#include <iostream>
using namespace std;

class Rechteck {
  
    private:
       int breite;
       int hoehe;
   public:
      Rechteck(int , int);
       int getBreite();
       int getHoehe();
       void setBreite ( int breite);
       void setHoehe(int hoehe);
       void flaeche();
};

Rechteck::Rechteck(int b, int h=4) {
   breite = b;
   hoehe = h;
}

int Rechteck::getBreite() {
  return breite;
}

int Rechteck::getHoehe() {
  return hoehe;
}

void Rechteck::setBreite( int b) {
  if (breite > 0) {
    breite = b;
  }
}

void Rechteck::setHoehe(int h) {
  hoehe = h;
}

void Rechteck::flaeche() {
  breite * hoehe;
}

main()
{
    int breite;
    int hoehe;
    int flaeche;
    cout <<"Geben sie die Breite an"<<endl;
    cin >>breite;
    cout << "Flaeche=" << flaeche <<endl;
    system("pause");
}
 

Technipion

Erfahrenes Mitglied
#2
Öhm, naja schau mal, du machst dir die ganze Arbeit und erstellst eine Klasse "Rechteck", aber in deiner main() benutzt du ja gar kein Rechteck sondern nur 3 eigenständige Integer. In Zeile 52 wo du flaeche ausgibst, hast du flaeche ja noch gar keinen Wert zugewiesen?
 

cwriter

Erfahrenes Mitglied
#5
Ich bin leider nicht der beste was Programmieren angeht und ich wollte mich halt mit der Aufgabe auf die Klausur vorbereiten.
Das ist durchaus löblich :)
Bedenke aber, dass Technipion der Legende nach auch eigene Verpflichtungen hat. Daher kann er nicht immer sofort antworten - Geduld :)

Oder ich übernehme einfach:
Ja. Du erstellst ja keine Instanz der Klasse "Rechteck" in der main. Woher soll nun der Wert kommen? Es macht auch keinen Sinn, die Fläche einfach so zu deklarieren, ohne einen Wert zuzuweisen - du willst ja die Fläche aus deiner Rechteck-Klasse (bzw. einer Instanz davon) bekommen.

aber das Ergebnis ist total falsch.
Wie überprüfst du das? Ausgabe? Die Variable "flaeche" hat keinen zugewiesenen Wert. Daher beinhaltet sie den Wert, der vorher an der Speicherposition stand - oft ist der 0, manchmal aber auch irgendetwas.

Dann hast du noch ein Problem im Konstruktor der Klasse selbst: breite > 0 wird dort gar nicht erzwungen.
Der Codestil ist etwas antiquitiert, es geht moderner und besser (=schnellerer Code), aber lösen wir erst einmal dein Problem:

C++:
main()
{
    int breite;
    int hoehe;
    int flaeche;
    cout <<"Geben sie die Breite an"<<endl;
    cin >>breite;

    // Hier fehlte es:
    Rechteck rechteck(breite, hoehe);
    flaeche = rechteck.flaeche();
    // Ende der Einfügung

    cout << "Flaeche=" << flaeche <<endl;
    system("pause");
}
Du wirst feststellen, dass die UML constraints nicht eingehalten werden. Grund: Der Konstruktor prüft keine Regeln. Wie du das fixen willst, überlasse ich erst einmal dir :)

Gruss
cwriter
 
#6
Ich habe natürlich auch selber versucht es zu lösen... :D aber hat auch nicht viel gebracht :D
Ich schaue mir mal dein Beitrag erstmal genauer an weil muss erstmal studieren was du meinst, trotzdem vielen Dank
C++:
    Rechteck R();
    B = R.getBreite();
    H = R.getHoehe();
    Flaeche = R.flaeche();
  
    cout <<"Geben sie die Breite an"<<endl;
    cin >>R.B;
    cout << "Flaeche=" << flaeche <<endl;
    system("pause");
 
#9
Habe es nun Rückgängig gemacht und trdz kommt der Fehler, habe danach gegoogelt viele meinten es fehlt ein ; oder eine Klammer ?

Und eine Frage hätte ich noch was genau bewirkt die Zeile 53 ?
da63bc6d8224fffe7e8cbf8faa88b6d4.png
 

cwriter

Erfahrenes Mitglied
#10
Habe es nun Rückgängig gemacht und trdz kommt der Fehler, habe danach gegoogelt viele meinten es fehlt ein ; oder eine Klammer ?
Der Fehler ist in der Klassendefinition. Dort hast du ein "void flaeche()", das ein "int flaeche()" sein müsste.
Dein
C++:
void Rechteck::flaeche() {
  breite * hoehe;
}

// Sollte sein:
int Rechteck::flaeche() {
  return breite * hoehe;
}
Ein Wort dazu: Du nutzt einen Compiler aus dem Jahr 2000? Bist du im Museum eingebrochen, um an den heranzukommen?
(Generell: Wenn nicht irgendein ewiggestriger Professor Uralt-Technologie vorschreibt, nimm eine moderne Alternative. Unter Windows macht Visual Studio am wenigsten Probleme, Linux ist mit g++ bzw. clang noch etwas besser aufgestellt. Diese gehen mit WSL auch auf Windows für kleine Programme sehr gut)
(Es macht alles ein bisschen schwieriger, da Borland nicht mehr wirklich genutzt wird - entsprechend sind die Fehlermeldungen kryptischer und man kann weniger nachschlagen - ganz zu schweigen von der hoffnungslos veralteten Sprache dahinter (das wird etwa C++99 sein - aktuell ist C++17, ganz neu C++20).

Und eine Frage hätte ich noch was genau bewirkt die Zeile 53 ?
Sie ruft den Konstruktor von Rechteck auf und erstellt damit eine Instanz von "Rechteck" namens "rechteck". Wenn dir das nicht klar ist, musst du es sagen - dann repetieren wir die Grundlagen.

Gruss
cwriter
 
#11
Ich nutzte halt das Programm was die Schule nutzt und einige Grundlagen sind mir recht klar nur das war mir schon immer unklar mit dem aufrufen vom Konstruktor und im Internet habe ich nicht so leichte Erklärungen gefunden aber das kann ich nachvollziehen
 

cwriter

Erfahrenes Mitglied
#12
Ich nutzte halt das Programm was die Schule nutzt
Ist mir schon bewusst - wir sind es uns gewohnt. Das Problem mit diesen veralteten Dingen ist, dass sie die Arbeit eher mühsamer machen. Klar, Lehrer/Professor sind oft zu faul oder haben keine Lust, neuere Technologien zu lernen, aber dann hört man wieder von allen Seiten, C++ sei eine alte und nutzlose Sprache, obwohl sie mehr könnte.
Ich wollte dich nur darauf hingewiesen haben ;)
So, fertig Offtopic.

das war mir schon immer unklar mit dem aufrufen vom Konstruktor und im Internet habe ich nicht so leichte Erklärungen gefunden aber das kann ich nachvollziehen
Belüge dich nicht selbst. Wenn etwas unklar ist, dann frage nach - wir kennen deinen Wissensstand ja nicht ;)

Konstruktoren sind spezielle Funktionen. Die Grundidee: Der Konstruktor soll die Instanz einer Klasse erstellen. Dazu kannst du in der Funktion annehmen, dass du schon genug Speicherplatz hast - was in diesem Speicher steht, ist aber beim Konstruktoraufruf undefiniert. Daher sollst du im Konstruktor die Werte initialisieren.
Nun gibt es Verschiedene Arten von Konstruktoren.

C++:
class Rechteck
{
public:
    Rechteck(); // Default Constructor
    Rechteck(const Rechteck& r); // Copy Constructor
    Rechteck(Rechteck&& r); // Move Constructor (in deiner C++-Version nicht verfügbar)

    Rechteck(int a, int b); // Generischer Konstruktor

    ~Rechteck(); // Destructor

};
Der Default-Constructor ist speziell: Er nimmt keine Argumente an. Wenn kein Konstruktor definiert ist, baut dir der Compiler einen davon. Sobald ein Generischer Konstruktor vorhanden ist, gibt es keinen davon mehr. Copy und Move sind speziell; Kurz gesagt übernehmen sie die Werte aus den Instanzen im Parameter.
Der Destruktor sollte aufräumen. Meist macht der in modernem C++ durch RAII nichts explizit (i.e. der Compiler macht dir den Destruktor), aber theoretisch solltest du daran Resourcen freigeben - hast du in deinem Beispiel nicht.

Zu der Nutzung:
C++:
/* Ruft Rechteck::Rechteck() auf - geht in deinem Beispiel nicht,
da der Default-Constructor implizit gelöscht wurde - du könntest ihn
mit "Rechteck::Rechteck() = default" wiederherstellen.*/
Rechteck rechteck;

/* Dasselbe */
Rechteck rechteck();

/* Ruft den entsprechenden Konstruktor auf */
Rechteck rechteck(123, 42);

/* Alternativ: Copy-Assigning - ist in alten Compilern langsamer. */
Rechteck rechteck = Rechteck();

/* Das alles war auf dem Stack; es geht auch auf dem Heap */
Rechteck* rechteck = new Rechteck(); // Oder anderer Konstruktor

/* Dann musst du aber manuell freigeben */
delete rechteck; // Ruft den Destruktor auf
Generell gilt bei diesen kleinen Dingen: Experimentiere! Füge unterschiedliche couts in unterschiedliche Konstruktoren ein und schaue, wann welcher Konstruktor aufgerufen wird.

Gruss
cwriter
 

Technipion

Erfahrenes Mitglied
#13
Ich nutzte halt das Programm was die Schule nutzt und einige Grundlagen sind mir recht klar nur das war mir schon immer unklar mit dem aufrufen vom Konstruktor und im Internet habe ich nicht so leichte Erklärungen gefunden aber das kann ich nachvollziehen
Zu meiner Zeit musste man so ziemlich alles aus Büchern lernen (EDIT: Okay das klingt falsch, sooo alt bin ich jetzt auch noch nicht /EDIT). Aber in diesen modernen Zeiten findet man (z.B. auf YouTube) durchaus gute Videos zu allen möglichen Programmierthemen. Versuch doch mal eine Suche nach "C++ Grundlagen" auf einer Videoplattform deiner Wahl. Ich vermute, dass du dort in wenigen Stunden die meisten Grundlagen verinnerlichen kannst, und das ist das wichtigste. Oft hapert es nämlich an den Basistechniken, aber wer die Grundlagen solide beherrscht findet sich schnell in neue Techniken und Denkweisen ein!

Was die Borland-Geschichte angeht: Leider kenne ich mindestens 3 Schulen und Unis, an denen mindestens bis 2016 noch Borland und/oder DevC++ eingesetzt wurde. Wenn du es ein bisschen moderner willst (aber immer noch im "Borland-Look") kannst du CodeBlocks probieren: Code::Blocks

Allerdings würde ich mich cwriter anschließen und dir - falls du auf Windows 10 bist - zu VisualStudio raten.

Klar, Lehrer/Professor sind oft zu faul oder haben keine Lust, neuere Technologien zu lernen, aber dann hört man wieder von allen Seiten, C++ sei eine alte und nutzlose Sprache, obwohl sie mehr könnte.
Auch hier leider ein trauriges "kann ich bestätigen" meinerseits. Bei vielen liegt es auch einfach daran, dass sie noch nichts von dem Umbruch in der C++-Welt gehört haben (ja, das ist möglich). Viele Bildungseinrichtungen in meinem Umfeld sind noch nicht einmal in C++11 angekommen. Viele sagen auch modernes C++ wäre zu schwer, allerdings kann ich das nicht bestätigen. Man muss ja nicht jedes Sprachdetail exzessiv nutzen. Aber ich finde es dämlich, dass man als C++-Anfänger deshalb z.B. auf den Genuss von Lambda-Ausdrücken verzichten muss. Ist ja nur meine Meinung. Die Schüler lernen dann später Java oder JavaScript und sind schockiert, wie intuitiv eine Sprache sich anfühlen kann.

Gruß Technipion