Verstehe ich nicht. Die Einführung des Schlüsselwortes "abstract" hat doch gerade bewirkt, dass man nicht mehr solche hässlichen Exceptions schmeißen muss mit "Not Implemented blabla". Der Compilier erzwingt die Implementation bei abgeleiteten Klassen und verbietet die Instanzierung der abstrakten Klasse. Wo soll da noch Raum sein, eine abstrakte Methode aufzurufen? Das muss schon gezielt mit Workarounds provoziert werden und da ist undefiniertes Verhalten für das Schwein doch genau das richtige. Eine saubere Fehlermeldung hat so jemand nicht verdient :<
Und wenn du den Fehler finden musst (weil du für den entsprechenden Teil des Quellcodes verantwortlich bist) und sich herausstellt, das in einer (viel zu tiefen, aber nicht von dir entworfenen) Klassenhierarchie im Konstruktor / Destruktor einer Klasse eine virtuelle Methode aufgerufen wird, obwohl der Teil des Objekts notwendig für diese Implementierung dieser Methode nocht nicht existiert / schon zerstört wurde?
Verstehe ich nicht. Die Einführung des Schlüsselwortes "abstract" hat doch gerade bewirkt, dass man nicht mehr solche hässlichen Exceptions schmeißen muss mit "Not Implemented blabla". Der Compilier erzwingt die Implementation bei abgeleiteten Klassen und verbietet die Instanzierung der abstrakten Klasse. Wo soll da noch Raum sein, eine abstrakte Methode aufzurufen? Das muss schon gezielt mit Workarounds provoziert werden und da ist undefiniertes Verhalten für das Schwein doch genau das richtige. Eine saubere Fehlermeldung hat so jemand nicht verdient :<
Hatte nen Fehler in nem library (luabind), das hat mir irgendwas zerfressen wenn noch shared referenzen übrig waren (), aber der lua_State geschlossen wurde. Lief dann im Endeffekt auf nen pure virtual function call raus. Wäre aber fast besser einfach zu crashen, dann hätte mein Filter nen Stacktrace machen können
Den Konstruktor hatte ich nur erwähnt, weil es für diesen genauso wie für den Destruktor gilt. Es kann aber tatsächlich dazu kommen:
Code:
//B.h - Arbeitsgruppe B
class A;
class B {
private:
A a;
public:
B ();
virtual void initialize () = 0;
};
//A.h - Arbeitsgruppe A
class A {
private:
B * b;
void initialize_b ();
public:
A (B * b);
};
//A.cc
A::A (B * b) : b (b) {
this->initialize_b ();
}
void A::initialize_b () {
this->b->initialize ();
}
//B.cc
B::B () : a (this) {}
Jetzt werde ein B erstellt.
(Es ist ein Beispiel, nicht mehr.)
Der Aufruf einer virtuellen Funktion ist natürlich warscheinlicher in einem Destruktor.
Nur ist das Beispiel absolut nichtssagend, denn von B kannst du sowieso keine Instanz erstellen, da die Klasse abstrakt ist.
Eine erbende Klasse muss initialize implementieren und damit ist das Problem dann auch wieder verschwunden.
Code:
class A
{
protected:
int x;
public:
A(int x)
: x(x) {}
int getX();
virtual void initialize_x() = 0;
virtual void uninitialize_x() = 0;
virtual ~A();
};
int A::getX()
{
return x;
}
A::~A()
{
uninitialize_x();
};
class B : public A
{
public:
B(int x);
virtual void initialize_x();
virtual void uninitialize_x();
virtual ~B();
};
B::B(int x)
: A(x)
{
initialize_x();
}
void B::initialize_x()
{
x *=2;
}
void B::uninitialize_x()
{
x /= 2;
}
B::~B()
{
//andere Aufräumarbeiten
A::~A();
}
int main()
{
A *binst = new B(10); //exception: B::B() ruft A::initialize_x() auf, weil der B Teil der Instanz noch nicht existiert
std::cout << binst->getX() << std::endl;
delete binst; //würde exception werfen, wenn das Programm so weit käme, weil A::~A() A::uninitialize_x() aufruft (da der B Teil bereits zerstört ist)
return 0;
}
Dieses sinnlose Klassenbeispiel verdeutlicht schon eher, was du mir sagen willst.
Und darauf antwortete ich schon passend: Calls zu virtuellen Funktionen haben weder im Kon- noch im Destruktor etwas verloren.
Eine erbende Klasse muss initialize implementieren und damit ist das Problem dann auch wieder verschwunden.
Ist es nicht, zum Zeitpunkt der Konstruktion bzw. Destruktion eines Bs ist der this-Zeiger (für den Aufruf virtueller Funktionen) immer ein Zeiger auf ein B und nicht auf eine von B abgeleiteten Klasse, damit kann die aufgerufene Funktion statisch ermittelt werden (siehe §12.7.4).
Ähnliches Verhalten bei Rückruffunktionen, welche im Destruktor aufgerufen werden können und auf das eigentliche Objekt zugreifen können.
Dein Beispiel tut übrigens nicht das, was du sagst, initialize_x wird erst nach Erstellen aller Felder von B aufgerufen (aber vor dem Konstruktor einer von B abgeleiteten Klasse, in B sollte initialize_x mit final versehen werden - und mit override).
Wenn eine virtuelle Funktion im Destruktor (oder im Konstruktor...) aufgerufen wird, so geschieht das für gewöhnlich nicht absichtlich, aber es passiert hin und wieder.
Ich habe nichts dazu gesagt, wann initialize_x() in Bezug auf die Felder von B aufgerufen wird - ist mir schon klar, dass der Konstruktorrumpf nach deren Konstruktion aufgerufen wird.
Aber ich habe mich vertan, natürlich muss initialize_x() ebenfalls in den Konstruktor von A und nicht in den von B. Dann stimmt der Sinn des Beispiels auch wieder.
Quote:
Ist es nicht, zum Zeitpunkt der Konstruktion bzw. Destruktion eines Bs ist der this-Zeiger (für den Aufruf virtueller Funktionen) immer ein Zeiger auf ein B und nicht auf eine von B abgeleiteten Klasse, damit kann die aufgerufene Funktion statisch ermittelt werden (siehe §12.7.4).
Ähnliches Verhalten bei Rückruffunktionen, welche im Destruktor aufgerufen werden können und auf das eigentliche Objekt zugreifen können.
Ist es wohl, wenn man sich an die Regel hält, virtuelle Funktionen in Konstruktoren nicht aufzurufen.
Nun gut, für den Fall der Fälle kannst du natürlich die Funktion dennoch definieren und eine Exception schmeißen lassen. Durch sauberes Programmieren geht es imho aber auch ohne, alleine mit den Absicherungen des Compilers bei Verwendung von abstrakten Klassen.
Ist es wohl, wenn man sich an die Regel hält, virtuelle Funktionen in Konstruktoren nicht aufzurufen.
Welches Problem ist verschwunden? Der Aufruf einer (da) rein virtuellen Funktion im Kon-/Destruktor? Wenn nichts aufgerufen wird, gibt es kein Problem, allerdings.
Dann dürfen also übergebene Funktionsobjekte nicht im Kon-/Destruktor aufgerufen werden, wenn es eine Klasse ist, die eine (u. U. private) Basisklasse mit einer (nicht notwendigigerweise rein) virtuellen Funktion hat?
Quote:
Originally Posted by MrSm!th
Nun gut, für den Fall der Fälle kannst du natürlich die Funktion dennoch definieren und eine Exception schmeißen lassen. Durch sauberes Programmieren geht es imho aber auch ohne, alleine mit den Absicherungen des Compilers bei Verwendung von abstrakten Klassen.
Eine Klasse ist (in C++) dann abstrakt, wenn sie mindestens eine nicht überschriebene rein virtuelle Funktion hat. Ob eine rein virtuelle Funktion dann tatsächlich auch eine Implementierung hat, ist dafür irrelevant, sie bleibt rein virtuell. Deshalb sind es ja gerade diese Sonderfälle mit Konstruktor und Destruktor und unangebracht statischen Typkonvertierungen (und natürlich Parallelität), welche zu solchen Aufrufen führen.
[help] new, delete, struct 01/23/2013 - C/C++ - 9 Replies Well, i have a structure, input and output functions, to get some data, now i would like to make the following:
-the user inputs a number (for which i need a dynamic memory reservation)
-and that number declares how many times the input should loop)
so for example the use puts in 4; it will run the input function 4 times he can tpye in stuff for 4 different companyes or w/e ... same with the output
Here is waht i ahev for now:
Struct Allokieren ? 12/08/2012 - C/C++ - 11 Replies Hallo,
Ich arbeite mich gerade durch das Buch C++ A - Z und bin gerade bei den Structuren, so
jetzt habe ich mir als beispiel ein kleines (Telefon Buch) gemacht ganz simpel (Vorname, Nachname, Geschlecht, Nummer)
Code
#include "stdafx.h"
using namespace std;
struct and classes 08/03/2012 - CO2 Programming - 6 Replies took about 50 pages reading about difference between structs and classes , so i thought it's good idea to sum up this for whoever want to get it with a spoon , feel free to correct or add information
at struct cannot have instance field initializers in structs
struct time
{
private int x = 5; // compile-time error
}
Structs cannot contain explicit parameterless constructors
struct time
{
[c++] struct 08/25/2011 - C/C++ - 2 Replies Kann closed werden. sry
Packet Struct's 05/02/2009 - Kal Online - 30 Replies Mir war langweilig hier nen beispiel wie ihr wie ich finde alles bischen besser aussehn lassen könnt :P
struct PacketsSend
{
DWORD VersionCheck;
DWORD Login;
DWORD Ping;
DWORD Skill;
DWORD Chat;