void reference?



  • Jede beliebige, genau wie ein void-Zeiger auf jede beliebige (naja, fast...) Variable zeigen kann. (So in etwa wie untypisierte const- oder var-Parameter in Delphi. Das einzige, was man damit machen kann, ist die Adresse zu nehmen.) Damit *könnte* man ein paar & und * sparen, aber vermisst habe ich sie in C++ eigentlich nie, meistens kann mans mit templates schöner bauen:

    void* addressOf(const void& anyObj) { return &anyObj; }
    wäre im Notfall
    template<T> void* addressOf(const T& anyObj) { return &anyObj; }
    


  • operator void schrieb:

    wäre im Notfall 
    template<T> void* addressOf(const T& anyObj) { return &anyObj; }
    

    Oder einfach nur

    reinterpret_cast<void*>(&anyObj)
    

    😃
    Ich sehe jedenfalls keine Notwendigkeit von void Referenzen.
    Ganz im Gegenteil. Referenzen haben hauptsächlich den Vorteil, dass das dahinterliegende Objekt immer gültig ist, da sie an existierende Objekte gebunden werden. Bei void darf man dann raten was sich dahinter verbirgt.
    Trotzdem können auch Referenzen missbraucht werden:

    template <class T>
    T& invalid_ref(T* x)
    {
    	T* tmp = NULL;
    	return *tmp;
    }
    
    int main()
    {
    	int a = 1;
    	int& b = invalid_ref(&a);
    	b = 2;	// oops :-o
    }
    


  • groovemaster2002 schrieb:

    Trotzdem können auch Referenzen missbraucht werden:

    template <class T>
    T& invalid_ref(T* x)
    {
    	T* tmp = NULL;
    	return *tmp; //hier oops
    }
    
    int main()
    {
    	int a = 1;
    	int& b = invalid_ref(&a);
    	b = 2;	// oops :-o //nein, hier kein oops
    }
    


  • groovemaster2002 schrieb:

    Ich sehe jedenfalls keine Notwendigkeit von void Referenzen.

    Wirklich notwendig ist wenig :p

    Ganz im Gegenteil. Referenzen haben hauptsächlich den Vorteil, dass das dahinterliegende Objekt immer gültig ist, da sie an existierende Objekte gebunden werden. Bei void darf man dann raten was sich dahinter verbirgt.

    Bei einer Referenz auf eine Klassen in einer Klassenhierarchie darf man auch raten, was sich nun wirklich hinter der Referenz verbirgt. Da verstehe ich das Argument nicht, void& wäre hier IMHO sogar schön einheitlich.

    Ich will's ja auch nicht einführen, aber wenn's von Anfang an in C++ vorhanden gewesen wäre, würde es mich vermutlich auch nicht stören...



  • operator void schrieb:

    Bei einer Referenz auf eine Klassen in einer Klassenhierarchie darf man auch raten, was sich nun wirklich hinter der Referenz verbirgt.

    Weil es unwichtig ist.
    Du machst
    foo.stirb()
    und foo stirbt, egal ob foo ein Vogel ein Löwe oder sonstwas ist.

    Da verstehe ich das Argument nicht, void& wäre hier IMHO sogar schön einheitlich.

    Insofern zieht dieses Argument nicht 😉



  • Shade Of Mine schrieb:

    Weil es unwichtig ist.
    Du machst
    foo.stirb()
    und foo stirbt, egal ob foo ein Vogel ein Löwe oder sonstwas ist.

    Das einzige, was man mit void& machen könnte, wäre die Adresse (den entsprechenden void*) zu nehmen - da wäre der dahinterliegende Typ auch nicht wichtig. Man hätte halt einen void* mit der üblichen Referenz-Syntax und dem Bonusvorteil, dass sich immer ein Objekt dahinter verbirgt. Bestimmt nicht gerade sinnvoll, aber "," überladen zu können war auch nicht gerade sinnvoll - nur einheitlich 😉 (Bis auf das fehlende void& sind die Regeln für die Konvertierungsmöglichkeiten von Zeigern und Referenzen doch AFAIK ziemlich identisch. Ob Referenzen und Zeiger überhaupt ähnlich sein *sollen*, ist dann wohl Geschmackssache...)



  • @Shade

    template <class T>
    T& invalid_ref(T* x)
    {
        T* tmp = NULL;
        return *tmp; //hier oops // nope
    }
    
    int main()
    {
        int a = 1;
        int& b = invalid_ref(&a);
        b = 2;    // oops :-o //nein, hier kein oops // definitiv hier oops
    }
    

    operator void schrieb:

    Bestimmt nicht gerade sinnvoll, aber "," überladen zu können war auch nicht gerade sinnvoll

    Ich geb ja zu, dass C++ einige Syntax Kinderkrankheiten hat (ich sag nur logical xor), aber mit void* kann man all das machen, was der eine oder andere mit void& machen möchte. Und man sollte sein Codedesign nochmal überdenken, wenn man void Objekte nach "aussen" geben muss.



  • template <class T>
    T& invalid_ref(T* x)
    {
        T* tmp = NULL;
        return *tmp; //hier oops // nope // dereferenzieren von NULL, also oops
    }
    
    int main()
    {
        int a = 1;
        int& b = invalid_ref(&a);
        b = 2;    // oops :-o //nein, hier kein oops // definitiv hier oops // quark
    }
    


  • Helium schrieb:

    template <class T>
    T& invalid_ref(T* x)
    {
        T* tmp = NULL;
        return *tmp; //hier oops // nope // dereferenzieren von NULL, also oops
    }
    
    int main()
    {
        int a = 1;
        int& b = invalid_ref(&a);
        b = 2;    // oops :-o //nein, hier kein oops // definitiv hier oops // quark
    }
    

    Warum sollte das gehen? b is doch ne Nullreferenz. Das kann doch nicht gehen...



  • Du hast vorher Null dereferenziert!



  • Mit dem oops ist es so eine Sache: dieses oops sagt aus: 'Undefiniertes Verhalten'

    Das bedeutet aber nicht, dass es auch schon hier abstürtzen muss. Es kann genausogut erst später oder auch garnicht Abstürzen.

    Das ist das gemeine an Undefiniertem Verhalten...



  • @Shade
    Hier gibt es kein Undefiniertes Verhalten. Das Programm stürzt immer ab, es sei denn man kann den Zugriff auf NULL-Speicher abfangen (kann ich im Moment net sagen ob und wie's geht).

    Ich weiss nicht, ob einige Leute den Code mal durch nen Debugger gejagt haben. Mir kommts so vor als ob das einige mal tun sollten. Vielleicht ist da ja auch jeder Compiler anders gepolt. Beim MSC jedenfalls ergibt sich das Verhalten so wie ich es erklärt habe und das ist für mich logisch.

    template <class T>
    T& invalid_ref(T* x)
    {
        T* tmp = NULL;
        return *tmp; // hier wird nicht NULL dereferenziert, sondern tmp
                     // und tmp ist eine gültige Variable mit gültigem Speicher
                     // dass tmp ein Zeiger ist, der auf NULL zeigt -> geschenkt
                     // letztendlich wird zwar ein NULL Objekt zurückgegeben,
                     // interessiert hier aber nicht, da ich weder lesend noch
                     // schreibend zugreife
    }
    
    int main()
    {
        int a = 1;
        int& b = invalid_ref(&a);
        b = 2;    // erst hier machts oops, weil ich jetzt auf ein NULL-Objekt
                  // schreibend zugreife
    }
    


  • groovemaster2002 schrieb:

    @Shade
    Hier gibt es kein Undefiniertes Verhalten. Das Programm stürzt immer ab, es sei denn man kann den Zugriff auf NULL-Speicher abfangen (kann ich im Moment net sagen ob und wie's geht).

    ... oder man hat ein System ohne Speicherschutz (MS-DOS, alte MacOS-Versionen etc.). Ich weiß nicht, was du unter undefiniertem Verhalten verstehst. Wir verwenden hier jedenfalls die Bedeutung aus dem C++-Standard, und nach der bedeutet das schlicht und einfach, dass der Standard keinerlei Forderungen an den Effekt stellt. Ein Absturz ist genauso zulässig wie irgendwas anderes, egal was.



  • groovemaster2002 schrieb:

    template <class T>
    T& invalid_ref(T* x)
    {
        T* tmp = NULL;
        return *tmp; // hier wird nicht NULL dereferenziert, sondern tmp
                     // und tmp ist eine gültige Variable mit gültigem Speicher
                     // dass tmp ein Zeiger ist, der auf NULL zeigt -> geschenkt
                     // letztendlich wird zwar ein NULL Objekt zurückgegeben,
                     // interessiert hier aber nicht, da ich weder lesend noch
                     // schreibend zugreife
    }
    
    int main()
    {
        int a = 1;
        int& b = invalid_ref(&a);
        b = 2;    // erst hier machts oops, weil ich jetzt auf ein NULL-Objekt
                  // schreibend zugreife
    }
    

    vielleicht solltest du nicht den debugger bemühen, sondern ein gutes Buch?

    Der Grund warum er dort oben nicht abschmiert ist wahrscheinlich der, dass Referenzen intern als Zeiger gehandhabt werden und keine Dereferenzierung stattfindet.

    Das ist aber Implementierungsabhängig...



  • per definitionem erzeugt das dereferenzieren eines zeigers, der auf NULL zeigt, undefiniertes verhalten - so der standard.
    und undefiniertes verhalten heißt, wie der name schon sagt, es kann geschehen, was geschehen will.



  • Der Compiler könnte die Dereferenzierung allerdings wegoptimieren, wenn er sieht, dass der Zeiger dereferenziert und dann direkt an die Referenz übergeben wird, das könnte ich mir auch vorstellen.



  • Der Standard erwähnt sogar ausdrücklich, dass in einem legalen Programm Referenzen auf Null nicht vorkommen können - weil man sie eben nur erzeugen kann, wenn man einen Nullzeiger dereferenziert (8.3.2)
    Egal ob der Compiler etwas wegoptimieren kann oder nicht - Wenn es so im Code steht, dann ist es einfach undefiniert, was herauskommt.



  • Ich habe beim MSVC sogar schon ne Referenz angelegt, die ich nicht
    initialisiert habe und erst spaeter auf eine Variable habe referenzieren
    lassen 😃

    Sollte er das eigentlich nicht _nicht_ schlucken? 🙄

    Soviel zum manchmal komischen Verhalten vom MSVC 😉

    mfg
    v R



  • Da würde mich ja interessieren, wie du das gemacht hast.



  • Bashar schrieb:

    Da würde mich ja interessieren, wie du das gemacht hast.

    Na das weisst du doch, war doch damals mit dir im chat und hatte den Code auch
    irgendwo auf rafb gepostet gehabt...mal sehn ob ich den wiederfinde. Weiss noch,
    dass der Borland-Compiler und der gcc den Code nicht geschluckt hat.

    War glaub ich sowas in der Art:

    int &i;
    
    int j;
    
    i = j;
    

    Ich schau nochmal nach, ansonsten kann ich ja nochmal MSVC installieren und
    das ganze nochmal machen.

    Wenn jemand das Visual Studio.net 2002 hat, sollte er das Ganze nachvollziehen
    koennen.

    mfg
    v R


Anmelden zum Antworten