C++ Fehler beim Einbinden von anderer Klasse - Achtung Anfänger

Ghost_Rider_R

Lieutenant
Registriert
Nov. 2009
Beiträge
773
Hallo zusammen,

als JAVA umsteiger möchte ich mich mal etwas an C++ versuchen und bin beim
erstellen einer 2. Klasse auf ein Problem gestoßen:

In JAVA erstelle ich ein objekt so:

Rechner rechner = new Rechner();
int ergebnis = rechner.berechnen(zahl1, zahl2);

Dies dürfte bei C++ etwas anders sein, da dies mit Headern arbeitet und hier habe ich

vermutlich einen Fehler gemacht, vielleicht könnt ihr mal drüber gucken vielen Dank.

Und ist es richtig, dass ich für jede Klasse einen eigenen Header schreiben muss?
oder können auch mehrere Klassen in einem Header zusammen gefasst werden?
und warum kann man auch Quelltext schon im Header definieren?

Vielen Dank

Grüße Ruff :)

Problem.png
 

Anhänge

  • Fehler.PNG
    Fehler.PNG
    9,3 KB · Aufrufe: 182
Wenn du eine Klassenmethode implementierst, dann muss es wie folgt aussehen:

Code:
int Rechner::berechnen(int zahl1, int zahl2)
{
  int ergebnis;
  ergebnis = zahl1 + zahl2;
  return ergebnis;
}

Bei dem Konstruktor war es richtig, jedoch fehlte es bei der Methode.
Somit war berechnen ein einfache Funktion die nur im Filescope von Rechner.cpp gültig und bekannt war.
 
TrebuTurbo schrieb:
Wenn du eine Klassenmethode implementierst, dann muss es wie folgt aussehen:

Code:
int Rechner::berechnen(int zahl1, int zahl2)
{
  int ergebnis;
  ergebnis = zahl1 + zahl2;
  return ergebnis;
}

Bei dem Konstruktor war es richtig, jedoch fehlte es bei der Methode.
Somit war berechnen ein einfache Funktion die nur im Filescope von Rechner.cpp gültig und bekannt war.


OK, das stimmt und gibt 100 Punkte :D

Eine etwas genauere anfängerfreundliche Erklärung währe noch ganz gut, damit ich auch verstehe warum es jetzt funktioniert :)

Desweiteren wäre noch offen:
Und ist es richtig, dass ich für jede Klasse einen eigenen Header schreiben muss?
oder können auch mehrere Klassen in einem Header zusammen gefasst werden?
und warum kann man auch Quelltext schon im Header definieren?
 
Ja jede Klasse gehört in eine eigene Header + Source Aufteilung.
Du könntest vererben.

Mit Quelltext im Header definieren meinst du den Konstruktor oder die DEFINE ?
 
Weil ichs im Folgenden öfters verwende:
- Funktionsdeklaration bedeutet, dass du dem Compiler eine Funktion bekannt machst. z.B. das "int berechnen(int zahl1, int zahl2);" in deiner Klassendeklaration ist eine Deklaration
- Funktionsdefinition bedeutet, dass du dem Compiler auch sagst, was die Funktion eigentlich macht, also den Inhalt. Die Funktionsdefinition ist also der Teil mit den {}.

Ruff_Ryders_R schrieb:
Eine etwas genauere anfängerfreundliche Erklärung währe noch ganz gut, damit ich auch verstehe warum es jetzt funktioniert
In deinem geposteten Code steht nur "int berechnen(...) { ... }". Das definiert eine Funktion namens berechnen im globalen "Scope". Im Gegensatz zu Java kann man nämlich auch Funktionen definieren, die zu keiner Klasse gehören. Der Linker hat sich dann darüber beschwert, dass die Funktion "berechnen" nicht in der Klasse Rechner definiert ist, sie aber aufgerufen wird. Du musst dem Compiler/Linker aber sagen, dass die berechnen-Funktion zur Klasse Rechner gehört. Eben, in dem du "Rechner::" vor den Funktionsnamen schreibst.

Ruff_Ryders_R schrieb:
Und ist es richtig, dass ich für jede Klasse einen eigenen Header schreiben muss?
oder können auch mehrere Klassen in einem Header zusammen gefasst werden?
Nein, das musst du nicht unbedingt. Du kannst mehrere Klassen in einer Header-Datei beschreiben. Das ist sogar gar keine schlechte Idee, wenn die Klassen "zusammengehören".

Ruff_Ryders_R schrieb:
und warum kann man auch Quelltext schon im Header definieren?
An einer Header-Datei ist erstmal nichts besonderes. Header-Dateien werden per Include vom Preprozessor einfach in die .cpp Datei geschrieben. also anstatt '#include "header.h"' wäre das Ergebniss das gleiche wenn du den Inhalt von der header.h per Copy&Paste an die gleiche Stelle schreibst.

Und dass man die Funktionsdefinition (also den "Quelltext" der Funktion) auch in die Klassendeklaration schreiben kann ist halt einfach ein C++-Feature.
 
Zuletzt bearbeitet:
stwe schrieb:
An einer Header-Datei ist erstmal nichts besonderes. Header-Dateien werden per Include vom Preprozessor einfach in die .cpp Datei geschrieben. also anstatt '#include "header.h"' wäre das Ergebniss das gleiche wenn du den Inhalt von der header.h per Copy&Paste an die gleiche Stelle schreibst.

Ok das macht im Zusammenhang dann natürlich auch alles Sinn, vielen Dank erstmal vorab dafür!

3 Dinge würden mich vielleicht noch interessieren:

Schreibt er durch das Include nur den Inhalt vom Header an die stelle, an der das include steht, oder auch die Definitionen der Methoden?

Woher weiß der Compiler, welche Klasse zur Headerdatei gehört bzw. welche Methode gemeint ist. Es könnten ja 2 Klassen in einer Headerdatei stehen, welche beide eine Methode int berechnenZahl() beinhalten. Oder weiß es das erst durch den Aufruf von Klassenname::objekt?

Und wie funktioniert das nun mit dem Objekt erstellen in C++, in JAVA funktionierte das ganze mit
Taschenrechner rechner = new Taschenrechner();

in C++ scheint ja auch irgendwie Taschenrechner rechner(); zu funktionieren. Könntet Ihr mir da vielleicht eine kurze Auflistung der Variationen/Möglichkeiten geben, wie man überlicherweise ein Objekt seiner eigenen Klasse erstellen kann?

Vielen Dank für eure Hilfe!

Grüße Ruff :)
 
Ruff_Ryders_R schrieb:
Schreibt er durch das Include nur den Inhalt vom Header an die stelle, an der das include steht, oder auch die Definitionen der Methoden?
Das #include sorgt nur dafür, dass der Inhalt der Header-Datei an der jeweiligen Stelle erscheint (wie gesagt, kann man sehr gut mit Copy'n'Paste vergleichen). Die Definitionen muss der Preprozessor nicht einfügen, denn das interessiert erst den Linker.

Ich führe das hier mal ein wenig aus. Den Compile-Prozess muss man nämlich verstehen, um sinnvoll mit C++ arbeiten zu können.
Das Compilen erfolgt in zwei (drei) Schritten:

1) Als allererstes läuft natürlich der Preprocessor und arbeitet alle #includes, #defines etc. ab. Dann wird eine .cpp-Datei zu einer Objekt-Datei kompiliert. Der Compiler nimmt also alle Funktionsdefinitionen der jewiligen .cpp-Datei und kompiliert sie. Falls jetzt ein Funktionsaufruf in der Datei steht, der in einer anderen .cpp-Datei definiert ist, dann interessiert den Compiler diese Funktionsdefinition nicht. Er muss nur die Signatur der Funktion wissen, um sie aufrufen zu können. Dazu reicht die Funktionsdeklaration und das ist der Grund, warum du die jeweiligen Header-Dateien einbinden musst.
2) Den zweiten Schritt bezeichnen man als Linken. Der Linker nimmt alle Objekt-Dateien und bastelt sie in eine ausführbare Datei (Die .exe-Datei die unter Windows am Ende entsteht). Der Linker sorgt am Ende dafür, dass der Funktionsaufruf auch die richtige Funktion aufruft.

Wie du also siehst, braucht der Compiler erstmal nur die Klassendeklaration. Wo die Funktionen letztendlich definiert sind ist egal, solange sie in einer der Objektdateien, die der Linker erhält drin steckt.

Ruff_Ryders_R schrieb:
Woher weiß der Compiler, welche Klasse zur Headerdatei gehört bzw. welche Methode gemeint ist. Es könnten ja 2 Klassen in einer Headerdatei stehen, welche beide eine Methode int berechnenZahl() beinhalten. Oder weiß es das erst durch den Aufruf von Klassenname::objekt?
Da greift wieder das Prinzip des "Scopes". Auf deutsch auch "Sichtbarkeitsbereich". Wenn du in beiden Klassen eine funktion "berechnenZahl" hast, dann existiert die Funktion nur in dem jeweiligen Scope. Die Funktionen Klasse1::berechnenZahl und Klasse2::berechnenZahl sind also zwei völlig verschiedene Funktionen, deshalb hat der Compiler da kein Problem das zu unterscheiden.

Ruff_Ryders_R schrieb:
Und wie funktioniert das nun mit dem Objekt erstellen in C++, in JAVA funktionierte das ganze mit
Taschenrechner rechner = new Taschenrechner();

in C++ scheint ja auch irgendwie Taschenrechner rechner(); zu funktionieren. Könntet Ihr mir da vielleicht eine kurze Auflistung der Variationen/Möglichkeiten geben, wie man überlicherweise ein Objekt seiner eigenen Klasse erstellen kann?
In der Tat funktionieren beide Methoden. Der Unterschied ist hier der jeweilige Speicherbereich. Im Gegensatz zu Java musst du dich bei C++ selbst darum kümmern, den "richtigen" Speicher zu verwenden.

Es gibt zwei verschiedene Arten: Den Stack (statisch) und den Heap (dynamisch). Variablen auf dem Stack legst du über "int a;" oder "Taschenrechner rechner;" an. Der Nachteil ist, dass dieser Speicher freigegeben wird, sobald du die Funktion verlässt (genauer: Den Scope).
Speicher auf dem Heap dagegen musst der Programierer selbst freigeben, d.h. er wird nicht automatisch gelöscht. Speicher auf dem Heap legt man mit "Taschenrechner *rechner = new Taschenrechner()" an. Beachte den "*" vor rechner. Die Variable "rechner" ist in diesem Fall ein Zeiger auf ein Taschenrechner-Objekt (Dein Beispiel ohne Stern funktioniert nicht). Speicher auf dem Heap musst du mit delete bzw. delete[] freigeben.

Interessante Links:
 
Ok, da hat mich schon mal ein sehr großes Stück weiter gebracht. Vielen Vielen Dank für die super ausführliche Erklärung, ich hoffe, dass auch nachfolgende User von diesem Beitrag profitieren können. Vielen Vielen Dank :-)
 
stwe schrieb:
Es gibt zwei verschiedene Arten: Den Stack (statisch) und den Heap (dynamisch). Variablen auf dem Stack legst du über "int a;" oder "Taschenrechner rechner;" an. Der Nachteil ist, dass dieser Speicher freigegeben wird, sobald du die Funktion verlässt (genauer: Den Scope).

Ich würde das einem Neuankömmling (besonders einem aus der Java-Welt) gegenüber jetzt nicht unbedingt pauschal als Nachteil bezeichnen, sonst kommt er noch auf die Idee, es sei in C++ immer besser, genau wie in Java alles mit new anzulegen. Es ist eben ein Unterschied ... ob's Vorteil oder Nachteil ist, hängt von der jeweiligen Situation ab. Möchte ich, dass ein innerhalb einer Funktion erstelltes Objekt auch nach Ablauf des Funktionsaufrufs noch weiterlebt? Dann muß ich es eben mit new anlegen. Brauch ich das Objekt nach Verlassen der Funktion nicht mehr? Dann ist es meistens ok, es einfach auf dem Stack anzulegen.
 
Zuletzt bearbeitet:
antred schrieb:
Ich würde das einem Neuankömmling (besonders einem aus der Java-Welt) gegenüber jetzt nicht unbedingt pauschal als Nachteil bezeichnen, sonst kommt er noch auf die Idee, es sei in C++ immer besser, genau wie in Java alles mit new anzulegen. Es ist eben ein Unterschied ... ob's Vorteil oder Nachteil ist, hängt von der jeweiligen Situation ab. Möchte ich, dass ein innerhalb einer Funktion erstelltes Objekt auch nach Ablauf des Funktionsaufrufs noch weiterlebt? Dann muß ich es eben mit new anlegen. Brauch ich das Objekt nach Verlassen der Funktion nicht mehr? Dann ist es meistens ok, es einfach auf dem Stack anzulegen.

Da hast du natürlich recht, das war etwas unglücklich formuliert.
 
Zurück
Oben