Abstrakte Klassen, Vererbung, Referenzen und Parameterügergabe
-
Vielen Dank! Das wars!
Gibts noch n Tipp hinsichtlich der Problematik mit Deklaration und Definition einer Klasse im selben File und Verwendung derselben Klasse in unterschiedlichen Dateien?
Bei den geposteten Sourcen von mir handelt es sich immer um .h Dateien, die z.T. mehrfach inkludiert werden.
Das führt im vorliegenden Fall zu ner Fehlermeldung vom Assembler, da er das Symbol für ApplicationC mehrfach findet.
Muss ich diese Klassen halt auch noch trennen in *.cc und *.h, so wie meine restlichen Klassen auch.
Ciao
-
Ja, Deklarationen immer in Header-Dateien (.h) und Implementationen in Source-Dateien (.cpp).
Nur wenn man z.B. private Klassen hat, kann man diese auch komplett in cpp-Dateien schreiben, aber niemals Implementationen in Header-Dateien schreiben.Aber es gibt zu jedem natürlich eine Ausnahme: Template-Klassen und -Methoden müssen in Header-Dateien geschrieben werden (bzw. in den Header-Dateien includiert werden - solange das Schlüsselwort "export" nicht unterstützt wird...).
-
Normalerweise kommt die Deklaration der Klasse in die Header-Datei und die Implementierung der Mehtoden kommt in eine zugehörige .cpp Datei.
Bei dir sieht das dann so aus:
ApplicationC.h:
#ifndef APPLICATIONC_H #define APPLICATIONC_H class ApplicationC { public: ApplicationC() {} virtual ~ApplicationC(); //Wieso war hier eigentlich das =0?? virtual void destroy() = 0; private: }; #endif
ApplicationC.cpp:
#include "ApplicationC.h" ApplicationC::~ApplicationC() { destroy(); }
TestApp.h:
#ifndef TESTAPP_H #define TESTAPP_H #include <iostream> #include "ApplicationC.h" class TestApplicationC : public ApplicationC { public: TestApplicationC(); ~TestApplicationC(); void destroy(); }; #endif
TestApp.cpp:
TestApplicationC::TestApplicationC() {} TestApplicationC::~TestApplicationC() { destroy(); } void TestApplicationC::destroy() { cout << "Destroy!" << endl; }
So in etwa...
Felix
EDIT: Dadurch gibt es dann nicht mehr die Linker-Fehler (der Assembler hat damit eigentlich wenig zu tun), wenn du einen Header mehrfach in verschiedenen Dateien hast, weil so keine mehrfachen Methodendefinitionen auftreten.
-
Das meinte ich ja damit, dass ich das bisher mit meinen anderen Klassen auch so gemacht habe (also Header und Implementierung in getrennte Dateien).
Aber wie ist das Vorgehen, wenn man beides in eine Datei steckt und diese dann in unterschiedlichen anderen Klassen verwenden will?
-
Achso, dann habe ich dich zuerst falsch verstanden.
Dafür gibt es drei "Möglichkeiten"
- Wieso sollte man das wollen, was du willst, wenn es anders doch auch, besser und ohne Mehraufwand geht?
Falls man es doch unbedingt machen will:
- Inline-Methoden benutzen
Beispiel:
Klasse1.h
#ifndef _KLASSE1_H #define _KLASSE1_H #include <iostream> class Klasse1 { public: inline virtual void TestMethode(); }; void Klasse1::TestMethode() { std::cout << "Klasse1"; } #endif
Klasse2.h
#ifndef _KLASSE2_H_ #define _KLASSE2_H_ #include "klasse1.h" #include <iostream> class Klasse2 : public Klasse1 { public: inline virtual void TestMethode(); private: }; void Klasse2::TestMethode() { std::cout << "Klasse2"; } #endif
main.cpp
#include "klasse1.h" #include "klasse2.h" int main(void) { Klasse2 testobjekt; testobjekt.TestMethode(); testobjekt.Klasse1::TestMethode(); return 0; }
Hierbei gibt es aber die Wechselwirkungen zwischen virtuellen und inline-Methoden, weil Methoden bei denen zur Laufzeit nicht feststeht, welche aufgerufen wird, nicht inline eingefügt werden können. Es läßt sich aber kompilieren...
- Anonyme Namespaces benutzen
Beispiel:
Klasse1.h#ifndef _KLASSE1_H #define _KLASSE1_H #include <iostream> class Klasse1 { public: virtual void TestMethode(); }; namespace { void Klasse1::TestMethode() { std::cout << "Klasse1"; } } #endif
Klasse2.h:
#ifndef _KLASSE2_H_ #define _KLASSE2_H_ #include "klasse1.h" #include <iostream> class Klasse2 : public Klasse1 { public: virtual void TestMethode(); private: }; namespace { void Klasse2::TestMethode() { std::cout << "Klasse2"; } } #endif
main.cpp:
#include "klasse1.h" #include "klasse2.h" int main(void) { Klasse2 testobjekt; testobjekt.TestMethode(); testobjekt.Klasse1::TestMethode(); return 0; }
Bei dieser Methode bin ich mir aber nicht sicher, ob das wirklich immer so toll funktioniert, die ist mir gerade nur eingefallen
Felix
-
Also ich bin jetzt aber doch der Meinung, dass die Klasse im gleichen Namespace wie ihre Methoden liegen muss - sonst klappt doch der Lookup schon garnicht mehr...
-
Phoemuex schrieb:
Achso, dann habe ich dich zuerst falsch verstanden.
Dafür gibt es drei "Möglichkeiten"
- Wieso sollte man das wollen, was du willst, wenn es anders doch auch, besser und ohne Mehraufwand geht?
Falls man es doch unbedingt machen will:
Danke.
Hm, war mir nicht sicher, ob das in C++ immer noch so gemacht wird mit Header- und Sorucedatei, oder ob das nur in C so sein sollte und in C++ andere Konventionen gelten. Hab kein größeres Problem damit immer 2 Files anzulegen.
-
Phoemuex schrieb:
- Anonyme Namespaces benutzen
dann muss auch die klasse im anonymen namespace sein - und dann handelt es sich nicht mehr um eine mehrfache definition derselben klasse (bzw. deren memberfunktionen) in mehreren ÜEs (die sind höchtens layout-komaptibel).
-
Ähm, also wegen dem nicht funktionieren:
Ich habe das mit dem Compiler, der bei Code::Blocks dabei ist genauso wie im Beispiel geschrieben und das Programm ließ
- sich kompilieren
- linken
- lief auch mit der erwarteten Ausgabe...
Kann aber halt sein, dass es trotzdem nicht Standard-konform ist...
-
also vc++8.0 hat schon mal was dagegen. und wenn ich ein bisschen stöbere finde ich bestimmt auch eine aussage im standard, die dieses verhalten unterstützt oder sogar verlangt. übrigens hast du es in diesem beispiel ja noch nicht mit mehreren ÜEs zu tun. funktioniert das linken dann immer noch ?
-
da wäre z.b. 9.3/2
A member function may be defined (8.4) in its class definition, in which case it is an inline member function (7.1.2), or it may be defined outside of its class definition if it has already been declared but not defined in its class definition. A member function definition that appears outside of the class definition shall appear in a namespace scope enclosing the class definition. Except for member function definitions that appear outside of a class definition, and except for explicit specializations of member functions of class templates and member function templates (14.7) appearing outside of the class definition, a member function shall not be redeclared.
-
Ok, ich sehs ein, ich hab ja auch direkt geschrieben, dass ich mir nicht sicher bin:
Phoemuex schrieb:
Bei dieser Methode bin ich mir aber nicht sicher, ob das wirklich immer so toll funktioniert, die ist mir gerade nur eingefallen
Aber weils sich (bei mir) kompilieren ließ, habe ich halt gedacht, ich poste es mal
-
Hallo nochmals,
also nach der Trennung in Header und Source Dateien klappts nun mit dem Kompilieren.
Gewundert hat es mich aber, dass ich für die pure virtual Methode destroy() auch eine Implementierung in der abstrakten Basisklasse angeben musste?!
Ich dachte, man muss nur für eine pure virtual Methode ne Implementierung angeben, nicht für alle?Ciao
-
ob eine funktion implementiert werden muss, hängt davon ab, ob sie von irgendwoher referenziert wird. prinzipiell muss keine deklarierte funktion (ob nun pure virtuell oder nicht) implementiert werden, solange sie nicht aufgerufen wird. in diesem speziellen fall ist das sogar undefiniertes verhalten. da bin ich doch erst vor ein paar tagen drüber gestolpert:
nochmal abstrakte klassen
-
OK, das leuchtet ein!
destroy() wird im Destruktor der Klasse gerufen!Die pure virtual Methoden müssen aber dennoch immer überschrieben werden, wenn die abgeleiteten Klassen nicht abstrakt sein sollen, auch wenn sie schon ne Implementierung in der Basisklasse haben, oder?
-
Reth schrieb:
OK, das leuchtet ein!
destroy() wird im Destruktor der Klasse gerufen!Die pure virtual Methoden müssen aber dennoch immer überschrieben werden, wenn die abgeleiteten Klassen nicht abstrakt sein sollen, auch wenn sie schon ne Implementierung in der Basisklasse haben, oder?
richtig. allerdings rufst du hier die funktion aus dem destruktor der basisklasse auf, und in diesem falle hast du eben ein problem, wie im anderen thread beschrieben.