Seite 1 von 1

Warum funktioniert folgender C++ Code?

Verfasst: 11.02.2013, 13:06
von Fitim
Hallo zusammen

Wann immer ich als Hobbyprogrammierer ein wenig Zeit finde, stöbere ich durch die interessante Welt von C++ und lerne ständig neue interessante Konstrukte.
Nun bin ich auf folgendes Konstrukt gestossen, welches virtuelle Methoden simuliert. Nur, warum funktioniert das? Zuerst einmal der Code:

Code: Alles auswählen

#include <iostream>
#include <ostream>

namespace ConsoleApplication
{
	template <typename Derived>
	class Test
	{
	public:
		Test() : derived(static_cast<Derived *>(this)) {}

		void CallMethods()
		{
			derived->FirstMethod();
			derived->SecondMethod();
		}

		void FirstMethod() const
		{
			::std::cout << "Test::FirstMethod()" << ::std::endl;
		}

		void SecondMethod() const
		{
			::std::cout << "Test::SecondMethod()" << ::std::endl;
		}

	private:
		Derived * derived;
	};

	class Demo : public Test<Demo>
	{
	public:
		Demo() {}

		void FirstMethod() const
		{
			::std::cout << "Demo::FirstMethod()" << ::std::endl;
		}

		/*
		void SecondMethod() const
		{
			::std::cout << "Demo::SecondMethod()" << ::std::endl;
		}
		*/
	};
}

int main()
{
	using namespace ::ConsoleApplication;

	Test<Demo> test;
	test.CallMethods();

	return 0;
}
Test::CallMethods() ruft die bestehende Demo::FirstMethod() Methode auf, und danach die eigene Test::SecondMethod(). Doch warum klappt das?
Es wird ja nirgendwo ein Demo-Objekt erzeugt oder irre ich mich da? Es muss dazu gesagt werden, dass ich sehr wenig Erfahrung mit Templates habe.

Liebe Grüsse

Re: Warum funktioniert folgender C++ Code?

Verfasst: 11.02.2013, 13:20
von Krishty
Klappt in C++03 nur durch Glück, weil – wie du gesagt hast – nirgends tatsächlich eine Demo-Instanz angelegt wird. Normalerweise verhindert man diesen Missbrauch, indem Tests K’toren protected gemacht werden. Du könntest es dann also nur durch eine abgeleitete Klasse instanzieren; d.h., indem du eine Demo-Instanz anlegst.

Für C++11 kenne ich die POD-Definition nicht gut genug um zu sagen, ob es standardkonform ist. Der Gedankengang ist: So lange die Klasse POD ist, darfst du so viel casten wie du willst, und es bleibt wohldefiniert. Für C++11 wurde die POD-Definition derart erweitert, dass auch Vererbung zugelassen ist. Das müsste ich also im Standard nachschauen; aber im Allgemeinen ist das, was du da zeigst, nicht erwünscht und, wie gesagt, in C++03 definitiv falsch.

Re: Warum funktioniert folgender C++ Code?

Verfasst: 11.02.2013, 13:45
von Fitim
Ahh, Danke.

Und wieder was gelernt :-)

Re: Warum funktioniert folgender C++ Code?

Verfasst: 11.02.2013, 14:45
von dot
Ich bin mir ziemlich sicher, dass das undefiniert ist, das es imo spätestens die strict aliasing rule verletzt. Abgesehen davon ist mir nicht ganz klar, inwiefern das "virtuelle Methoden simulieren" soll... ;)

Re: Warum funktioniert folgender C++ Code?

Verfasst: 11.02.2013, 14:50
von Krishty
Wo werden die verletzt, abgesehen davon, dass die Demo-Instanz nicht existiert? Demo erbt von Test<Demo>, also muss jede Instanz von Test<Demo> als mit jeder Demo-Instanz überlappend angenommen werden, sofern das nicht explizit durch weitere Analyse ausgeschlossen werden kann.

Re: Warum funktioniert folgender C++ Code?

Verfasst: 11.02.2013, 17:59
von dot
Imo wird hier

Code: Alles auswählen

derived->FirstMethod();
auf ein Objekt über ein lvalue, das nicht seinem dynamischen Typ entspricht, zugegriffen.

Re: Warum funktioniert folgender C++ Code?

Verfasst: 11.02.2013, 20:40
von simbad
Das wird solange funktionieren, solange nicht auf Attribute zugegriffen wird, die die Existenz einer Instanz verlangen. So einen Müll habe ich schonmal in sicherheitsrelevanter Software gefunden. die haben dreist mit einem null-pointer auf Methoden zugegriffen. Das ging, eben aus oben genannten Grund.
Auf meinen Hinweis, das die Methode vielleicht besser als Funktion aufgehoben wäre, weil die ja eh nix mit dem Objekt zutun hätte, hat man meinen Vorschlag abgelehnt und die Methode static deklariert. Was das nun besser gemacht hat war mir nicht klar.

Re: Warum funktioniert folgender C++ Code?

Verfasst: 14.02.2013, 22:59
von eXile
simbad hat geschrieben:Das wird solange funktionieren, solange nicht auf Attribute zugegriffen wird, die die Existenz einer Instanz verlangen. So einen Müll habe ich schonmal in sicherheitsrelevanter Software gefunden. die haben dreist mit einem null-pointer auf Methoden zugegriffen. Das ging, eben aus oben genannten Grund.
Ich wollte noch etwas nur peripher Relatiertes hinzufügen: Auch in zur Kompilierzeit evaluierten Kontexten muss man mit C++11 keine static_cast<MyStruct *>(0)->… mehr schreiben, sondern kann declval (welches wohl via add_rvalue_reference implementiert werden kann) benutzen:

Code: Alles auswählen

#include <iostream>
#include <typeinfo>
#include <utility>

struct MyStruct
{
    int MyMethod(char);
};

int main(int argc, char * argv[])
{
    // What does MyStruct::MyMethod return?
    std::cout << typeid(decltype(std::declval<MyStruct *>()->
                                 MyMethod(std::declval<char>()))).name() << std::endl;
   
    return 0;
}
(Natürlich gab es auch vorher, da ja alles in so einem Fall zur Kompilierzeit stattfand, keine Sicherheitsprobleme, und es gibt keine Polymorphie. Aber vielleicht kannte jemand hier obigen Trick noch nicht, und ich wollte ihn mal zeigen.)