Wurde Methode überschrieben?



  • Ist es möglich herauszufinden ob eine Memberfunktion von einerabgeleiteten Klasse überschrieben wurde?

    Ist es irgendwie möglich einen Methodenzeiger zu bekommen wenn man ein Objekt hat? Also nur den Methodenzeiger in der Vtable nachzuschlagen aber nicht aufzurufen? Dann wäre es nämlich möglich durch einen Vergleich mit dem Methodenzeiger der Basisklasse zu bestimmen ob eine Methode überschrieben wurde.

    Notfals macht es auch ein Hack. Muss nicht portable sein.



  • Ben04 schrieb:

    Ist es möglich herauszufinden ob eine Memberfunktion von einerabgeleiteten Klasse überschrieben wurde?

    Nö.

    Ist es irgendwie möglich einen Methodenzeiger zu bekommen wenn man ein Objekt hat? Also nur den Methodenzeiger in der Vtable nachzuschlagen aber nicht aufzurufen?

    Nö. Das ist implementierungsspezifisch.

    Notfals macht es auch ein Hack. Muss nicht portable sein.

    Hmm. Hol Dir einen Zeiger auf das gegebene Objekt und schau Dir in einem Debugger die Bytes an, die am Anfang stehen. Die kannst Du im Debugger ja dann mit Speicheradressen vergleichen um zu sehen, ob sie auf Methoden zeigen.



  • Ist es möglich herauszufinden ob eine Memberfunktion von einerabgeleiteten Klasse überschrieben wurde?

    wenn folgendes gilt:
    a) du kennst die Signatur und den Namen der Methode
    b) die Methode und alle eventuellen Überschreibungen sind public
    c) du kennst den genauen Typ der Klasse für die du die Nachfrage stellst
    dann geht sowas:

    struct X {
    	virtual void x() {}
    	virtual ~X() {}
    };
    
    struct Y : X {
    public:
    	virtual void x() {}
    };
    
    struct Z : X {
    };
    
    template <class T>
    struct OverridesX {
    	static char test(void (T::*p)());
    	template <class U>
    	static int test(void (U::*p)());
    
    	enum {
    		value = sizeof(test(&T::x)) == sizeof(char)
    	};
    };
    
    int main() {
    	cout << OverridesX<X>::value << endl;	// 1
    	cout << OverridesX<Y>::value << endl;	// 1
    	cout << OverridesX<Z>::value << endl;	// 0
    }
    

    Andernfalls und falls du die entsprechende Methode nicht aufrufen darfst, bist du was Standard-C++ angeht verloren.



  • 1. Warum braucht man sowas?

    2. Wie geht das?



  • HumeSikkins schrieb:

    ...

    Genial, darf ich fragen woher du das hast?



  • Wenn man nicht selber darauf kommt, dann lernt man sowas aus Modern C++ Design 🙂



  • @Hume Dein Code ist aber auch nicht standard-konform da sizeof(int) != sizeof(char) nicht gegeben ist.

    Und nein ich kenne den Type leider nicht. Ich hab nur ein upgecastetes Objekt.

    1. Warum braucht man sowas?

    Wenn man C Objekthierrachien wrappen will und dabei C Objekte weiter unterstützen muss. In reinem C++ Code sollte man sowas nicht brauchen.

    Hab das Problem nun gelöst indem ich um das Problem herumsteuere.



  • Ben04 schrieb:

    @Hume Dein Code ist aber auch nicht standard-konform da sizeof(int) != sizeof(char) nicht gegeben ist.

    Normal verwendet man auch ein struct Dummy{ char twoChars[2] }; um zu garantieren, dass das eine größer als das andere ist. Hume wollte sicher nur etwas Platz sparen (da gewöhnliche plattformen größere ints haben).



  • babbb schrieb:

    HumeSikkins schrieb:

    ...

    Genial, darf ich fragen woher du das hast?

    Aus meinem Kopf, deshalb kann es nicht genial sein. In der C++ Community gibt es aber sicher irgendwo ein Codeschnipsel, der die fehlende Genialität ergänzt.

    @Hume Dein Code ist aber auch nicht standard-konform da sizeof(int) != sizeof(char) nicht gegeben ist.

    Das ist natürlich korrekt. Allerdings war das auch nur beispielhaft zusammengeschustert, weshalb ich auf den korrekten Weg (siehe Beitrag von lolz) verzichtet habe. Und bei diesen typedefs a la:
    typedef char (&yes)[1];
    typedef char (&no)[2];
    muss ich immer so lange über die richtige Klammerung nachdenken.

    Und nein ich kenne den Type leider nicht. Ich hab nur ein upgecastetes Objekt

    Falls es sich bei deiner Hierarchie um eine einfache Hierarchie handelt (keine virtuelle/mehrfach Vererbung) und du dich auf das Objektlayout deines Compilers verlassen kannst (sprich: der Code unportabel sein darf), dann kannst du natürlich den von dir angedeuteten Weg gehen.

    Beispiel für einen Compiler, der vptr und vtables verwendet und bei dem der vptr immer das erste Element eines Objekts ist:

    struct X {
    	virtual void x() {}
    	virtual ~X() {}
    };
    
    struct Y : X {
    public:
    	virtual void x() {}
    };
    
    struct Z : X {
    };
    
    struct VTable {
    	typedef void (X::*pmf)();
    	pmf arr[0];
    };
    
    bool overrides(X* obj, int virtNum) {
    	static X x;
    	return (*reinterpret_cast<VTable**>(&x))->arr[virtNum]
    		!= (*reinterpret_cast<VTable**>(obj))->arr[virtNum];
    }
    
    int main() {
    	X x;
    	Y y;
    	Z z;
    	cout << overrides(&x, 0) << endl;	// 1
    	cout << overrides(&y, 0) << endl;	// 1
    	cout << overrides(&z, 0) << endl;	// 0	
    }
    

    Hab das Problem nun gelöst indem ich um das Problem herumsteuere.

    Das scheint mir der bessere Weg zu sein.


Anmelden zum Antworten