[C++] Problem mit überladener Member-Funktion

daemon777

Lt. Commander
Registriert
Dez. 2003
Beiträge
1.371
Hallo,
ich bräuchte mal wieder Hilfe. Und zwar geht es um ein kleines Programm welches ich angefangen habe zu schreiben. Das Problem ist folgendes:
Ich habe 2 Klassen. Die eine Klasse ist voll von Datenstrukturen wie 2dimensionalen Arrays. Die 2. Klasse soll nun nicht weiter machen als eine Datei zu öffnen und auszulesen und anschließend diese ausgelesenen Dateien in meine 1. Klasse schreiben.

Nun habe ich schon festgestellt dass ich nicht ohne weiteres einfach eine Klasse in einer Funktion als Parameter übergeben kann. Seltsamerweise funktioniert das aber auch nicht mit einer Referenz :(

Die erste Klasse:

Code:
#include <string.h>

class karte
{

public:

	std::string dateiname;
	std::string levelname;

	char feld1[2000][2000];
	
	karte();
};

karte::karte()
{
	dateiname="default.lvl";
	levelname="Level1";
}

Und die zweite:

Code:
#include <string>
#include <fstream.h>


using namespace std;

class datei
{

	
public:
	string dateiname;
	
	datei();
	
	int show();
	int dread(string datei, karte mkarte);
};

datei::datei()
{
	dateiname="default.lvl";	
};

//das ist die kritische Funktion
int datei::dread(string datei,karte mkarte)
{
	
	return 1;
};

In der Main-Methode steht nun folgendes:

Code:
datei mdatei;
	karte mkarte;
	
	std::cout<<mdatei.dateiname ;
	std::cout<<"\nEs wird versucht aus einer Datei auszulesen!\n";
	mdatei.dread("test.lvl",mkarte);

Das funktioniert aber nicht :(

Könnt ihr mir sagen woran das liegt, wie ich das Problem umgehen kann oder wie ich es zum laufen bekomme ?

Danke schon mal im Vorraus
 
Hallo daemon777,

nur eine Kleinigkeit, Du übergibst keine Klasse sondern ein Objekt einer Klasse. Das ist ein wesentlicher Unterschied. Klassen kann man z.B. an Templates als Parameter übergeben.

Ansonsten habe ich auf Anhieb auch keinen Fehler gesehen. Ausser wie bereits gesagt, wenn die zwei Klassen in zwei Dateien sind, wird das ganze nicht kompilieren, weil die includes für die jeweils andere Klasse fehlen.

MfG

Arnd
 
Header ist inkludiert. Ich hab es jetzt mal so versuche:

Code:
//in der main
	datei mdatei;
	karte mkarte;
	
	mdatei.dread("test.lvl",*mkarte);

// und in der laden-klasse
int datei::dread(string datei,karte &mkarte)
{
	
	return 1;
};

Dabei bekomme ich folgende Fehlermeldung:

D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\laden.h(26) : error C2511: 'dread' : Überladene Member-Funktion 'int (class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class karte &)' nicht in 'datei' gefunden
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\laden.h(8) : Siehe Deklaration von 'datei'
D:\SPIELE PROGGEN\TEST\DATEIEN\maps\main.cpp(21) : error C2100: Zeigeroperation ungueltig
Fehler beim Ausführen von cl.exe.

maps.exe - 2 Fehler, 0 Warnung(en)

Gibt es keine Möglichkeit eine Klasse oder Referenz/Zeiger in einer Methode zu übergeben ohne gleich komplizierte Überladungsoperatoren zu schreiben ?
 
das einzige was mir noch aufgefallen ist sind deine nicht korrekten includes( stl header haben kein endung!). ansonsten funktioniert der code vom ersten post wunderbar, deine änderungen sollten auch compilieren.

meine dateien sehen so aus:

karte.h
Code:
#ifndef KARTE_H
#define KARTE_H

#include <string>

class karte
{

public:

	std::string dateiname;
	std::string levelname;

	char feld1[2000][2000];

	karte();
};


#endif // KARTE_H

karte.cpp
Code:
#include "karte.h"


karte::karte()
{
	dateiname="default.lvl";
	levelname="Level1";
}

datei.h
Code:
#ifndef DATEI_H
#define DATEI_H

#include <string>
#include <fstream>

#include "karte.h"


class datei
{


public:
	std::string dateiname;

	datei();

	int show() {}
	int dread(std::string datei, karte mkarte);
};


#endif // DATEI_H

datei.cpp
Code:
#include "datei.h"

datei::datei()
{
	dateiname="default.lvl";	
};

//das ist die kritische Funktion
int datei::dread(std::string datei,karte mkarte)
{

	return 1;
};

main.cpp
Code:
#include <iostream>

#include "datei.h"
#include "karte.h"


int main(int argc, char *argv[])
{
	datei mdatei;
	karte mkarte;

	std::cout<<mdatei.dateiname ;
	std::cout<<"\nEs wird versucht aus einer Datei auszulesen!\n";
	mdatei.dread("test.lvl",mkarte);
}

ansonsten hoffe ich das deine finalen klassen ein etwas besseres design haben und die datein etwas aufgeräumter sein werden :P.
 
Zuletzt bearbeitet:
Hi daemon,

dein Problem ist wie's ausschaut, dass die Methodendefinition im Header und in der Cpp nicht übereinstimmen.
Desweiteren solltest Du die mkarte auf jeden Fall per Referenz übergeben, da ansonsten von dem Objekt eine Kopie gemacht wird und alle Änderungen die in dread gemacht werden nach Ablauf der Methode für die Katz sind.
Sollte dann in etwa so aussehen: int dread( const std::string&, mkarte& karte);
Wenn Du dread aufrufst, brauchst Du weder zu dereferenzieren noch die Adresse vom reingesteckten Objekt zu ermitteln. In der Main siehst dann in etwa so aus:

Code:
datei d;
std::string name = "datei.txt";
karte k;
d.dread( name, k);

Davon mal abgesehen... Member in Klassen macht man niemals public (immer! private), weil Du sonst gegen ein Paradigma der objektorientierten Programmierung verstößt (Kapselung). Mit der Zeit und Erfahrung merkt man schnell, warum das so schlimm ist. Also nur nicht die Hoffnung aufgeben, wenn es Dir jetzt nicht wirklich klar ist ;)
 
Bis auf das was 7H3 N4C3R am Ende gesagt hat leuchtet mir das ein :D

Allerdings habe ich jetzt einen neuen Fehler der bis zur letzten Version meines Codes noch nicht aufgetreten ist :freak:

-------------------Konfiguration: maps - Win32 Release--------------------
Kompilierung läuft...
main.cpp
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\karte.h(4) : error C2011: 'karte' : 'class'-Typ-Neudefinition
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\karte.h(17) : error C2084: Funktion '__thiscall karte::karte(void)' hat bereits einen Funktionsrumpf
D:\SPIELE PROGGEN\TEST\DATEIEN\maps\main.cpp(17) : error C2264: 'karte::karte' : Fehler in der Funktionsdefinition oder Funktionsdeklaration, Funktion nicht aufgerufen
Fehler beim Ausführen von cl.exe.

maps.exe - 3 Fehler, 0 Warnung(en)

Ich habe in der karte.h datei nicht geändert. Also verstehe ich den Fehler auch nicht :(
 
das problem ist das du deinen code nicht postest ;). so ist es ziemlich schwer zu sagen wo genau der fehler liegt.

vermutlich ist du meine dateien nicht ordentlich kopiert. wenn du einen header mehrfach includierst ohne das du defines benutzt wird deine klasse natürlich auch mehrfach definiert - und das is in c++ verboten.

aber wiegesagt, ohne code kann man da schlecht was genaues zu sagen.


zu den membern: member variablen in klassen sollten niemals public sein wie 7H3 N4C3R schrieb. stattdessen solltest du sie am besten private machen und dazu 2 public methoden zum ändern und zurückgeben des wertes.

EDIT:
in deinem fall sind getter und setter wohl sogar überflüssig - oder soll man die variablen von aussen spähter noch ändern können?

besser wäre also das hier:

datei.h
Code:
#ifndef DATEI_H /* diese zeilen sind SEHR wichtig! */
#define DATEI_H /* denn dadurch wird die klasse nur einmal definiert selbst wenn du den header in mehrere dateien includierst! */

#include <string>
#include <fstream>

#include "karte.h"


class datei
{
public:
	datei();

	int show() {}
	int dread(const std::string& datei, karte& mkarte); /* da du den string warscheinlich eh nicht ändern wirst hab ich den auch einfach mal const gemacht. wenn du nicht vor hast eine variable zu ändern sollte die immer const sein! */

	inline std::string dateiname() const; /* das ist der getter, damit kann man spähter von aussen auslesen welche datei mit der klasse geladen wurde. damit sie praktisch genausoschnell wie das direkte auslesen der variable ist ist, sie als inline definiert. methoden die keine daten verändern sollten immer als const definiert werden. */

private: // member variables
	std::string dateiname;
};

/* implementierung für den getter. inline methoden sollten immer im header implementiert werden!  */
inline std::string datei::dateiname() const
{
	return dateiname;
}

#endif // DATEI_H /* die zeile ist auch wichtig, damit das ifdef von ganz obhen richtig funktioniert  */

der code ist jetz nicht getestet, sollte aber eigentlich funktionieren.
 
Zuletzt bearbeitet:
Ich hab das jetzt so gemacht nur dass ich erstmal die getter und setter Funktionen weggelassen habe. Beim Kompilieren habe ich keinen Fehler. Aber direkt nachdem das Programm geöffnet wurde stürtzt es auch wieder ab.

Die main Datei:
Code:
#include <iostream>
#include <windows.h>
#include <string>
#include <karte.h>
#include <laden.h>

using namespace std;

int main()
{
	std::cout<<"Dies ist ein Testprogramm zum laden von Maps\n";
	Sleep(3000);
	std::cout<<"Ein Objekt des Types Datei wird angelegt!"<<endl;
	Sleep(1000);
	//Objekt erstellen
	datei mdatei;
	
	std::cout<<"mdatei konnte erstellt werden"<<endl;
	Sleep(1000);
	karte mkarte;
	
	std::cout<<"mkarte konnte erstellt werden"<<endl;
	
	Sleep(1000);
	std::cout<<"\nEs wird versucht aus einer Datei auszulesen!\n";
	mdatei.dread("test.lvl",mkarte);
	
	Sleep(1000);
	std::cout<<"dread konnte ausgeführt werden"<<endl;
	Sleep(3000);
	return 0;
}

Die Karte- Datei
Code:
#ifndef _karte_
#define _karte_

#include <string>

class karte
{

private:

	std::string dateiname;
	std::string levelname;

	char feld1[2000][2000];
	
public:
	karte();
};

karte::karte()
{
	karte::dateiname="default.lvl";
	karte::levelname="Level1";
};

#endif

Und zum Schluss der Lade-Header
Code:
#ifndef _datei_
#define _datei

#include <string>
#include <fstream>
#include <karte.h>

using namespace std;

class datei
{
private:
	std::string dateiname;
		
public:
	
	datei();
	
	int show();
	int dread(std::string datei, karte &mkarte);
};

datei::datei()
{
	datei::dateiname="default.lvl";	
};

int datei::dread(std::string datei,karte &mkarte)
{
	
	return 1;
};

#endif

Übrigens wird nicht einmal das erste cout ausgegeben. Das programm stürtzt also schon vor der ersten Zeile der main Methode ab.

Ich weiß irgendwie nicht mehr weiter. Hoffe mal ihr wisst woran das liegt.
 
Ah okay klar. Funktionen / Methoden, die Du in Headern aber außerhalb einer Klasse definierst, müssen mit dem Schlüsselwort inline markiert sein. Einfach davor schreiben.

Ahso zu den 10 Zeichen :) Beim Bearbeitungsgrund steht etwas *g*
 
Hallo daemon777,

Code:
datei::datei()
{
	datei::dateiname="default.lvl";	
};

Ich würde das in die CPP Datei stecken. Ausserdem ist der Prefix "datei::" vor der Variablen dateiname überflüssig, bzw für mich verwirrend. Ich assoziere damit dann sofort eine statische Membervariable aus einer anderen Klasse.

Schliesslich bist Du aktuell in der Klasse Datei und die Variable ist auch nicht statisch. Bei den couts, ... ist das ok, da Du diese Objekte ja auch von aussen referenzierst.

MfG

Arnd
 
Das ändert aber leider alles nichts daran dass mein Programm abstürtzt :(
Ich poste jetzt nochmal den Code so wie er jetzt ist:

Code:
//Zu erst die main

#include <iostream>
#include <windows.h>
#include <string>
#include <karte.h>
#include <laden.h>

using namespace std;

int main()
{
	std::cout<<"Dies ist ein Testprogramm zum laden von Maps\n";
	Sleep(3000);
	std::cout<<"Ein Objekt des Types Datei wird angelegt!"<<endl;
	Sleep(1000);
	//Objekt erstellen
	datei mdatei;
	
	std::cout<<"mdatei konnte erstellt werden"<<endl;
	Sleep(1000);
	karte mkarte;
	
	std::cout<<"mkarte konnte erstellt werden"<<endl;
	
	Sleep(1000);
	std::cout<<"\nEs wird versucht aus einer Datei auszulesen!\n";
	mdatei.dread("test.lvl",mkarte);
	
	Sleep(1000);
	std::cout<<"dread konnte ausgeführt werden"<<endl;
	Sleep(3000);
	return 0;
}


// Jetzt der Karten Header

#ifndef _karte_
#define _karte_

#include <string>

class karte
{

private:

	std::string dateiname;
	std::string levelname;

	char feld1[2000][2000];
	
public:
	inline karte();
};

inline karte::karte()
{
	
};

#endif


//Und jetzt der Laden Header
#ifndef _datei_
#define _datei

#include <string>
#include <fstream>
#include <karte.h>

using namespace std;

class datei
{

private:
	std::string dateiname;
		
public:
	
	inline datei();
	
	inline int dread(std::string datei, karte &mkarte);
};

inline datei::datei()
{
		
};

inline int datei::dread(std::string datei,karte &mkarte)
{
	
	return 1;
};
#endif

Wie kann es denn passieren dass ich keine fehlermeldung bekomme und das Programm dann einfach abstürtzt ?
Ich hatte das schon mal bei einem etwas größeren Projekt und ohne eine wirkliche Änderung ist das Programm dann jedes Mal abgestürtzt.

irgend etwas muss doch jetzt noch an diesem code falsch sein.
 
Zuletzt bearbeitet: (Vergessen einen Teil des Codes zu posten)
Hallo daemon777,

ich habe das ganze mal probiert und es bestätigt sich einfach immer wieder, das man auch an Kleinigkeiten sofort meckern sollte :-).

Das erste was mir an Deinem Programm nicht gefallen hat ist das hier:

char feld1[2000][2000];

Das bedeutet konkret das Du eine Variable mit 4MB Stackbedarf hast. Per Default hat ein Windows Programm jedoch einen Stack von 1 MB.

Das bedeutet Du hast zwei Möglichkeiten. Entweder einfach in den Linker Optionen die Stacksize hochsetzen am besten auf 8MB. Oder Du legst den Speicher dynamisch an per new. Was ich für die bessere Lösung halte.

Bei debuggen bekomme ich übrigens einen Stackoverflow. Das ist ein ziemlich deutlicher Hinweis darauf das entweder etwas überschrieben wurde, eine Rekursion besteht oder eben einfach zu viel Speicher benutzt wird.

MfG

Arnd
 
das mit dem new benutzte ich nie weil ich es bisher für unnützt hielt :D

Wie sähe das denn aus ?
char feld1 = new char[1000][1000] ??
 
Nein, eher so:

Code:
char** feld1 = new char* [1000];
for (int i=0; i<1000; i++)
  feld1[i] = new char [1000];
New erzeugt einen Speicherbereich, und gibt die Adresse (Pointer) zurück. Da du aber ein zweidimensionales Feld willst, brauchst du einen Pointer der auf Pointer zeigt. Du kannst aber stattdessen auch einfach ein eindimensionales Array erzeugen, und den Index "von Hand" berechnen. ;)
Zum Beispiel so:
Code:
char* feld1 = new char [1000*1000];
Wenn du jetzt auf Index x-y zugreifen möchtest, geht das im oberen Beispiel mit feld1[x][y], und im unteren Beispiel mit feld1 [x*1000+y].
Vorteil bei den Methoden mit new ist, dass das Array auch dynamisch angelegt werden kann, die Länge und Breite von 1000 auch erst zur Laufzeit festgelegt werden kann. D.h. dass an deren Stelle auch Variablen stehen können. :)

@Arnd
Was hat das mit der Default-Stacksize auf sich? Ich mein das Programm läuft ja trotzdem, warum sollte man also auf eine Stacksize von <1MB achten? :confused_alt:
 
Hallo Green Mamba,

die Frage verstehe ich nicht ganz. Das Programm läuft eben nicht, sondern es stürzt ab.

Zumindest der MS Compiler im VC6 legt in seinen Compileroptionen per Default eine Stacksize von 1MB fest. Das ein Programm wenn es diese Grösse überschreitet einfach abstürzt leuchtet doch ein, oder?

Getestet habe ich es mit VS Studio 2003. Bei Defaulteinstellung -> Absturz, bei Einstellung auf 8MB Programm läuft.

Schliesslich wird dann irgendetwas, höchstwahrscheinlich der Codebereich mit Daten überschrieben. Sicher hängt das auch davon ab, wie der Compiler bzw. der Linker den Speicherbereich anlegt bzw ob er ihn reserviert oder nicht.
Wird der Speicherbereich vorab reserviert stürzt das Programm sofort ab, wird er nicht reserviert würde das Programm erst bei Nutzung des Speichers abstürzen.

Und 1MB ist ja normalerweise für die meisten Anwendungen ausreichend und bei dem Speicherausbau heutiger Rechner auch kein Problem.

Und das Problem ist allgemeiner Natur, hat also sicher nichts nur mit MS Compilern zu tun. Auch unter Linux und GCC wird es einen Stack geben, der eine bestimmte Grösse hat. Wo die liegt weiss ich nicht, aber das Programm wird auch da abstürzen.

Zu dem Thema habe ich mal ein bisschen gegoogelt:

http://www.dirac.org/linux/gdb/02-Memory_Layout_And_The_Stack.php

MfG

Arnd
 
Zuletzt bearbeitet:
Mein Karten Header sieht jetzt so aus:

Code:
#ifndef _karte_
#define _karte_

#include <string>

class karte
{

private:

	std::string dateiname;
	std::string levelname;

	char** feld1 = new char* [1000];
		
public:
	inline karte();
	inline ~karte();
};

inline karte::karte()
{
	for (int i=0; i<1000; i++)
		feld1[i] = new char [1000];	
};

inline karte::~karte()
{
	delete feld1[];	
};

#endif

Und die Fehlermeldung:

--------------------Konfiguration: maps - Win32 Release--------------------
Kompilierung läuft...
main.cpp
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\karte.h(14) : error C2258: Ungueltige Syntax fuer rein virtuelle Methode; '= 0' erforderlich
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\karte.h(14) : error C2252: 'feld1' : Nur Funktionen koennen rein virtuell deklariert werden
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\karte.h(24) : error C2065: 'feld1' : nichtdeklarierter Bezeichner
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\karte.h(24) : error C2109: Index benoetigt ein Feld oder einen Zeigertyp
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\karte.h(24) : error C2440: '=' : 'char *' kann nicht in 'int' konvertiert werden
Diese Konvertierung erfordert einen reinterpret_cast-Operator oder eine Typumwandlung im C- oder Funktionsformat
D:\SPIELE PROGGEN\TEST\DATEIEN\MAPS\karte.h(29) : error C2059: Syntaxfehler : ']'
Fehler beim Ausführen von cl.exe.

maps.exe - 6 Fehler, 0 Warnung(en)

Ich hoffe dass ihr mir noch mal ein gutes Stück weiterhelfen könnt.
 
Zurück
Oben