Basisklasse übergeben?
-
Pattern benutzt Base, also muss Base vor Pattern definiert sein.
Vor der Base reicht eine Vorwärtsdeklaration von Pattern.
(Siehe auch hier)
-
sorry - ich meinte diesen code:
#include <iostream> class Pattern { public: void foo(Base* base) { base->To_Pattern(this); } }; // die main class Base { public: virtual void To_Pattern(Pattern*& P) = 0; }; class D1 : public Base { public: void To_Pattern(Pattern *& P) { std::cout << "D1:Base" << std::endl; //auf P arbeiten } }; class D2 : public Base { public: void To_Pattern(Pattern*& P) { std::cout << "D2:Base" << std::endl; // auf P arbeiten } }; int main(int argc, char** argv) { Base * base = NULL; D1 d1; D2 d2; base = &d1; Pattern pat; pat.foo(base); return 0; }mit folgender meldung:
g++ -o out main.cpp main.cpp:7: Fehler: »Base« wurde nicht deklariert main.cpp: In member function »void Pattern::foo(int*)«: main.cpp:9: Fehler: Abfrage des Elementes »To_Pattern« in »* base«, das vom Nicht-Klassentyp »int« ist main.cpp: In function »int main(int, char**)«: main.cpp:46: Fehler: keine passende Funktion für Aufruf von »Pattern::foo(Base*&)« main.cpp:7: Anmerkung: Kandidaten sind: void Pattern::foo(int*)
-
Gleiches Problem.
-
ja ich habs gesehen. langsam kommen wir zu der meldung die mein haupt-code produziert.
Ich bitte nochmals um folgende betrachtung:
#ifndef My_H #define My_H #include "Base.h" class Pattern { public: void foo(Base* base) { base->To_Pattern(this); } }; #endif#ifndef Bs_H #define Bs_H #include "Pattern.h" // die main class Base { public: virtual void To_Pattern(Pattern*& P) = 0; }; #endif#ifndef D1_H #define D1_H #include "Pattern.h" #include "Base.h" class D1 : public Base { public: void To_Pattern(Pattern *& P) { std::cout << "D1:Base" << std::endl; //auf P arbeiten } }; #endif#ifndef D2_H #define D2_H #include "Pattern.h" #include "Base.h" class D2 : public Base { public: void To_Pattern(Pattern*& P) { std::cout << "D2:Base" << std::endl; // auf P arbeiten } }; #endif#include "Base.h" #include "Pattern.h" #include "D1.h" #include "D2.h" int main(int argc, char** argv) { Base * base = NULL; D1 d1; D2 d2; base = &d1; Pattern pat; pat.foo(base); return 0; }Mit folgender meldung:
Pattern.h:10: Fehler: »Base« wurde nicht deklariert Pattern.h: In member function »void Pattern::foo(int*)«: Pattern.h:12: Fehler: Abfrage des Elementes »To_Pattern« in »* base«, das vom Nicht-Klassentyp »int« ist main.cpp: In function »int main(int, char**)«: main.cpp:22: Fehler: keine passende Funktion für Aufruf von »Pattern::foo(Base*&)« Pattern.h:10: Anmerkung: Kandidaten sind: void Pattern::foo(int*)
-
Da sind wir ja schon nen Schritt weiter

Pattern hat eine Funktion, die mit einem Base arbeitet.
Base ist du diesem Zeitpunkt noch nicht bekannt, wird ja erst nachher deklariert.
Mit einer Vorab-Deklaration kommst du nicht weiter, da do nur der Typenname bekannt ist, aber nicht seine Schnittstelle.Einzige Lösung:
Dein "main.cpp" in verschieden Header (.h) und Implementiereungen (.cpp) aufteilen. Dann reicht im Header eine Forward-Deklaration, im .cpp wird dann der jeweils andere Header mit includiert.
Im Prinzip sollte es reichen, Pattern von den restlichen Klassen zu separieren.@Drakon:
Danke!Fehler: Als linker Operand einer Zuweisung wird L-Wert erfordertkommt wenn ich "this = new Test;" versuche.
An eine Funktion das this übergeben macht aber keine Probleme! Das hatte ich so nicht erwartet.
Aber funktionieren tut es nur, wenn das Objekt mit "new" erstellt wurde. Sonst -> SegFault!Interessant fand ich auch, dass das Objekt an der selben Stelle im Speicher erstellt wurde! Egal ob vorher delete oder nicht!
#include <iostream> using namespace std; struct Test; void modify(Test*); struct Test { int num; void modifyThis() { num = 3; cout << this << endl; modify(this); cout << num << endl; } ~Test() { cout << "destroy " << this << endl; } }; void modify(Test* t) { cout << "modifiy " << t << endl; delete t; // Auskommentieren, dann verschwindet der Segfault, die Adresse ändert sich nicht! t = new Test; } int main() { Test* t = new Test; t->modifyThis(); cout << "*** " << t << endl; Test t3; t3.modifyThis(); // <== Segfault!!! }Also Vorsicht beim Überschreiben des this-Pointers.
-
puh...also danke schonmal...
Einzige Lösung:
Dein "main.cpp" in verschieden Header (.h) und Implementiereungen (.cpp) aufteilen. Dann reicht im Header eine Forward-Deklaration, im .cpp wird dann der jeweils andere Header mit includiert.
Im Prinzip sollte es reichen, Pattern von den restlichen Klassen zu separieren.aj aj aj...ich überlege wie ich das ändern kann weil mir der lösungsvorschlag zu aufwändig/seltsam ist. Evtl. ist bei mir falsches design die grundlage...
hmm...
-
//Base.h #ifndef Bs_H #define Bs_H //#include "Pattern.h" // nein! class Patern; // ja! // die main class Base { public: virtual void To_Pattern(Pattern*& P) = 0; }; #endifAn eine Funktion das this übergeben macht aber keine Probleme! Das hatte ich so nicht erwartet.
Aber funktionieren tut es nur, wenn das Objekt mit "new" erstellt wurde. Sonst -> SegFault!Nein, nein, nein. Du verstehst das etwas völlig falsch!
void modify(Test* t) // hier wird der this Zeiger kopiert { cout << "modifiy " << t << endl; delete t; // das ist ganz klar böse, wenn du ein automatisches Objekt hast t = new Test; // das weist t einen neuen Zeiger zu und nicht dem Objekt, auf welches t vorher gezeit hat. }Ich hoffe du verstehst das jetzt, ansonsten muss ich wirklich auch noch mit ASCII Art anfangen. :p
-
Hier einfach mal die Lösung zu deinem Problem.
Es geht nicht einfacher bei solchen gegenseitigen Abhängigkeiten.Base.h
#ifndef BASE_H #define BASE_H class Pattern; class Base { public: virtual void To_Pattern(Pattern* P) = 0; }; class D : public Base { public: void To_Pattern(Pattern * P); }; #endifBase.cpp
#include <iostream> #include "Base.h" #include "Pattern.h" void D::To_Pattern(Pattern * P) { std::cout << "D1:Base" << std::endl; //auf P arbeiten }Pattern.h
#ifndef PATTERN_H #define PATTERN_H class Base; class Pattern { public: void foo(Base* base); }; #endifPattern.cpp
#include "Pattern.h" #include "Base.h" void Pattern::foo(Base* base) { base->To_Pattern(this); }main.cpp
#include "Base.h" #include "Pattern.h" int main(int argc, char** argv) { Base * base = 0; D d1; base = &d1; Pattern pat; pat.foo(base); return 0; }Kompilieren:
$ g++ -c Pattern.cpp $ g++ -c Base.cpp $ g++ main.cpp -o main Base.o Pattern.o $ ./mainvoid modify(Test* t) // hier wird der this Zeiger kopiert { cout << "modifiy " << t << endl; delete t; // das ist ganz klar böse, wenn du ein automatisches Objekt hast t = new Test; // das weist t einen neuen Zeiger zu und nicht dem Objekt, auf welches t vorher gezeit hat. }In der letzten Zeile dein Kommentar:
Ich habe auch gedacht, dass t nun auf ein neues Objekt, was ja definitiv an einer anderen Stelle im Speicher liegen müsste, zeigt.
Die ganzen Test-Ausgaben vor, während und nach dem Modify zeigen mir in dem Fall aber ein anderes Bild:0x603010 modifiy 0x603010 destroy 0x603010 0 *** 0x603010Es ist IMMER die selbe Adresse, demnach wird die selbe Speicherstelle überschrieben... Oder bin ich da jetzt wieder total daneben?
Ein späteres
t = new Test; cout << t << endl; t = new Test; cout << t << endl;liefert wie erwartet
0x603030 0x603050
-
Mach mal das hier:
void modify(Test* t) // hier wird der this Zeiger kopiert { cout << "modifiy " << t << endl; delete t; t = new Test; cout << "modifyed t: " << t << endl; delete t; // jetzt auch wieder aufräumen }Du kannst t ja gar nicht kennen ausserhalb der Funktion.. Du hast klar immer den gleichen Wert ausgegeben.
... cout << this << endl; (1) modify(this); (2) } ~Test() { cout << "destroy " << this << endl; (3) } ... 0x603010 (1) modifiy 0x603010 (2) (innerhalb von modify) destroy 0x603010 (3) 0 ?? *** 0x603010 ??Mit dem obigen Code müsste zwischen 2 und 3 ein anderer Zeiger ausgegeben werden.
-
Hmmm...
#include <iostream> using namespace std; struct Test; void modify(Test*); struct Test { void modifyThis() { cout << "=== Test::modifiyThis() (this == " << this << ") === " << endl; modify(this); cout << "after modify() " << this << endl; cout << "======" << endl; } ~Test() { cout << "destroy " << this << endl; } }; void modify(Test* t) { cout << "modifiy " << t << endl; delete t; t = 0; t = new Test; cout << "modified t: " << t << endl; delete t; } int main() { Test* t = new Test; t->modifyThis(); cout << "*** " << t << endl; delete t; }Das finale "delete t" in modify() beschert mir einen segfault beim finalen delete in main()...
Ebenso wie ein weiterer Aufruf von modifyThis().
Ich find das lustig, weil ich ohne weiteres noch mit dem Objekt arbeiten konnte, auf Member zugreifen usw. aber ein zweites delete geht in die Hose.Außerdem, egal an welcher Stelle ich noch die Adresse ausgeben lasse (in Test::modifyThis(), modify() oder main()), immer ist es die Selbe...
Du kannst t ja gar nicht kennen ausserhalb der Funktion.. Du hast klar immer den gleichen Wert ausgegeben.
Hmm, ich kann doch belibig Zeiger kopieren und dann vergleichen, das sollte bei egal wie gearteten Aufrufen bei gleichen Adressen das gleiche Ergebnis liefern.
Ob ich in einer Memberfunktion jetzt "this" mit einem übergebenen Pointer vergleiche oder zwei Pointer in einer freien Funktion oder direkt, oder ...Ich glaub ich muss da nochmal drüber schlafen...
Vllt. küsst mich die Erkenntnis ja noch
Grüße und Danke
-
Ich habe doch gesagt, dass das obige delete böse per se ist.
#include <iostream> using namespace std; struct Test; void modify(Test*); struct Test { void modifyThis() { cout << "=== Test::modifiyThis() (this == " << this << ") === " << endl; modify(this); cout << "after modify() " << this << endl; cout << "======" << endl; } ~Test() { cout << "destroy " << this << endl; } }; void modify(Test* t) { cout << "modifiy " << t << endl; // ok, gibt das gleiche, wie this //delete t; // böse, böse böse! t = 0; // ok t = new Test; // ok cout << "modified t: " << t << endl; //ok, sollte was anderes ausgeben delete t; // ok } int main() { Test* t = new Test; // ok t->modifyThis(); //ok cout << "*** " << t << endl; //ok, gibt das gleiche, wie this delete t; // wenn das innere delete aktiviert ist, wird hier der speicher erneut zerstört und das gibt den fehler! }
-
Lästermaul schrieb:
Das finale "delete t" in modify() beschert mir einen segfault beim finalen delete in main()...
Das ist nicht verwunderlich, der Speicher wurde ja bereits freigegeben.
Lästermaul schrieb:
Ich find das lustig, weil ich ohne weiteres noch mit dem Objekt arbeiten konnte, auf Member zugreifen usw. aber ein zweites delete geht in die Hose.
Ziehe keine Schlüsse aufgrund solcher Aktionen. Sobald ein Objekt zerstört wurde, resultiert der Zugriff darauf in undefiniertem Verhalten. Das bedeutet, es ist reines Glück, falls das Programm abstürzt.
Lästermaul schrieb:
Hmm, ich kann doch belibig Zeiger kopieren und dann vergleichen, das sollte bei egal wie gearteten Aufrufen bei gleichen Adressen das gleiche Ergebnis liefern.
Ob ich in einer Memberfunktion jetzt "this" mit einem übergebenen Pointer vergleiche oder zwei Pointer in einer freien Funktion oder direkt, oder ...Du übergibst eben eine Kopie des Zeigers, deshalb führt das
t = new Test;inmodify()nicht dazu, dass der originale Zeiger neu zugewiesen wird. Derthis-Zeiger kann nämlich nicht verändert werden.
-
@Lästermaul
Lies Dir mal _genau_ durch was "by Value" bedeutet. Wenn Du erinen Zeiger by Value übergibst (was Du bei deinem Methodenaufruf nunmal machst) dann wird für den Scope der Methode ein temporärer Zeiger angelegt. Alle Veränderungen des Zeigers innerhalb der methode (new, delete etc) finden NUR auf diesem temporären Zeiger statt und sind daher außerhalb der Methode nicht relevant.
Natürlich können mehrere Zeiger auf das gleiche Objekt im Speicher zeigen, weist Du aber einem der Zeiger mittels new ein neues Objekt zu, hat das _keine_ Auswirkung auf die anderen Zeiger die nach wie vor auf das alte Objekt zeigen.
Um die Änderung am Zeiger innerhalb der Methode auch außerhalb sichtbar zu machen mußt Du den Zeiger "by Reference" übergeben, als sowas in der Art:
void modify(Test*& t) { }
-
Nexus schrieb:
Lästermaul schrieb:
Ich find das lustig, weil ich ohne weiteres noch mit dem Objekt arbeiten konnte, auf Member zugreifen usw. aber ein zweites delete geht in die Hose.
Ziehe keine Schlüsse aufgrund solcher Aktionen. Sobald ein Objekt zerstört wurde, resultiert der Zugriff darauf in undefiniertem Verhalten. Das bedeutet, es ist reines Glück, falls das Programm abstürzt.
Und genau das denk ich mir schon die ganze Zeit!!! Undefiniertes Verhalten.
Dann ist der Kommentar von loks bezügl. delete auf eine Kopie eines Zeigers auch falsch. Ich bin erleichtert!Du übergibst eben eine Kopie des Zeigers, deshalb führt das
t = new Test;inmodify()nicht dazu, dass der originale Zeiger neu zugewiesen wird. Derthis-Zeiger kann nämlich nicht verändert werden.Dass man this (den this-Zeiger) nicht ändern kann, hab ich doch auch gesagt, trotzdem wurde mir wiedersprochen

Das mit der Kopie des Zeigers ist mir jetzt auch klar.
Werd mir das nochmal in Ruhe anschauen, ob ich das alles jetzt richtig begreife...
-
loks hat schon Recht.
Du musst unterscheiden zwischen dem Zeiger als Objekt an sich und dem Speicherbereich, auf den er zeigt.
int* a = new int(4); int* b = a; // beide Zeiger zeigen auf 4 delete b; // gibt die angeforderte Vier frei b = 0; // a ist nun NICHT Null.Wenn du einen Zeiger als Value-Parameter übergibst, d.h.
void modify(Test* t), ist das eine Kopie, wie hier im Beispielb. Änderungen am Speicherbereich dahinter sind von ausserhalb der Funktion sichtbar, Änderungen am Zeiger selbst jedoch nicht. Und zu letzteren gehört nun malt = new Test.
-
Nexus schrieb:
loks hat schon Recht.
[...]Wenn du einen Zeiger als Value-Parameter übergibst, d.h.
void modify(Test* t), ist das eine Kopie, wie hier im Beispielb. Änderungen am Speicherbereich dahinter sind von ausserhalb der Funktion sichtbar, Änderungen am Zeiger selbst jedoch nicht. Und zu letzteren gehört nun malt = new Test.loks schrieb:
Alle Veränderungen des Zeigers innerhalb der methode (new, delete etc) finden NUR auf diesem temporären Zeiger statt und sind daher außerhalb der Methode nicht relevant.
Die allgemeine Formulierung ist aber irreführend.
Es mag sein dass das delete für den Zeiger (!) außerhalb von modify() irrelevant ist. Trotzdem ist es im Allgemeinen nicht irrelevant, denn das delete verändert vllt. nicht den Zeiger, aber den Speicherbereich - Das Objekt auf welches der Zeiger zeigt wird gelöscht. Ab hier sind Zugriffe auf das Objekt undefiniert.
-
Okay, da stimme ich dir zu, seine Aussage ist in der Tat etwas missverständlich.
-
Lästermaul schrieb:
Nexus schrieb:
loks hat schon Recht.
[...]Wenn du einen Zeiger als Value-Parameter übergibst, d.h.
void modify(Test* t), ist das eine Kopie, wie hier im Beispielb. Änderungen am Speicherbereich dahinter sind von ausserhalb der Funktion sichtbar, Änderungen am Zeiger selbst jedoch nicht. Und zu letzteren gehört nun malt = new Test.loks schrieb:
Alle Veränderungen des Zeigers innerhalb der methode (new, delete etc) finden NUR auf diesem temporären Zeiger statt und sind daher außerhalb der Methode nicht relevant.
Die allgemeine Formulierung ist aber irreführend.
Es mag sein dass das delete für den Zeiger (!) außerhalb von modify() irrelevant ist. Trotzdem ist es im Allgemeinen nicht irrelevant, denn das delete verändert vllt. nicht den Zeiger, aber den Speicherbereich - Das Objekt auf welches der Zeiger zeigt wird gelöscht. Ab hier sind Zugriffe auf das Objekt undefiniert.ja, stimmt, das delete erzeugt hier ein Problem je nah Reihenfolge. Wenn man erst new aufruft und dann wieder delete passiert ausserhalb gar nix. Ruft man aber erst ein delete auf wie in den Beispielen hier, dann erzeugt man Probleme.
-
Also ich komme mit der Lösung von Lästermaul immer noch nicht zurecht.
Wenn ich jetzt in der KLasse D (die unter anderem templateklasse ist)
innerhalb der methode To_Pattern das Pattern P verändern will schreit der compiler mit:Fehler: invalid use of undefined type »struct Pattern« Fehler: forward declaration of »struct Pattern«Hier nochmal mein angepasstes Minimalbeispiel:
#ifndef BASE_H #define BASE_H #include <iostream> class Pattern; class Base { public: virtual void To_Pattern(Pattern* P) = 0; }; template <class T> class D : public Base { public: void To_Pattern(Pattern * P) { std::cout << "D1:Base" << std::endl; P->var = 2; //std::cout<< P->var << std::endl; //auf P arbeiten } }; #endif#ifndef PATTERN_H #define PATTERN_H class Base; class Pattern { public: Pattern() { var = -1; } void foo(Base* base); int var; }; #endif#include "Pattern.h" #include "Base.h" void Pattern::foo(Base* base) { base->To_Pattern(this); }#include "Base.h" #include "Pattern.h" #include <iostream> int main(int argc, char** argv) { Base * base = 0; D<int> d1; base = &d1; Pattern pat; pat.foo(base); std::cout<< pat.var << std::endl; return 0; }
-
Weil du natürlich nicht alles so übernommen hast, sondern in Base.h direkt auf Pattern zugreifen willst, obwohl das nur ne Forward-Declaration ist.
Am besten die Implementierung von Base in eine Base.cpp auslagern, dann "schreit" der Compiler auch nicht mehr.
Bei mir ist der übrigens total ruhig. Nur der CPU-Lüfter dreht etwas schneller (trotzdem nicht laut) wenn ich gaaaanz viel kompilieren tu.