Struct/MAP in struct speichern?

09/14/2013 11:58 MrSm!th#16
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 :<
09/14/2013 12:50 Tasiro#17
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?
09/14/2013 13:36 MrSm!th#18
Virtuelle Methode im Konstruktor? Tasiro pls..
09/14/2013 13:49 Master674b#19
Quote:
Originally Posted by MrSm!th View Post
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 ([Only registered and activated users can see links. Click Here To Register...]), 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 :D
09/15/2013 01:09 Tasiro#20
Quote:
Originally Posted by MrSm!th View Post
Virtuelle Methode im Konstruktor?
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.
09/15/2013 02:42 MrSm!th#21
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.
09/15/2013 11:50 xXGerdXx#22
struct EventGift
{
DWORD dwID;
int nCount;
int nPercentage;
};
struct EventMonster
{
DWORD dwID;
int nInerval;
int nPercentage;
map<DWORD,EventGift> m_aEventGift;
};
class AutoEvent
{
public:
map<DWORD, EventMonster> m_aEventMonster;
AutoEvent();
~AutoEvent();

void ReadInc();
void Process();
};
09/15/2013 12:22 Tasiro#23
Quote:
Originally Posted by MrSm!th View Post
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.
09/15/2013 19:11 MrSm!th#24
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.
09/15/2013 20:40 Tasiro#25
Quote:
Originally Posted by MrSm!th View Post
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 View Post
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.
09/17/2013 21:43 Jopsi332#26
mein goot ist der thread ausgeatet, habs nun geschafft. lag wirklich an dem ZeroMemory

danke!

#closerrequest
09/19/2013 22:40 xxfabbelxx#27
closed on request