Zeiger



  • Die_Gute_Fee schrieb:

    Genau.
    Bei Pointern und Referenzen handelt es sich um Variablen, deren Wert eine Adresse ist.

    .. wobei Letztere selbst nicht mehr adressierbar ist.

    Gruß,

    Simon2.


  • Mod

    Referenzen sind keine Objekte, mithin erst recht keine Variablen.



  • Referenzen sind nur Aliase, die nur im C++-Sourcecode existieren. Das ist der Sinn einer Referenz. Halt nur "ein anderer Name für eine Variable im Sourcecode". Klar, wenn ein Compiler so "doof" ist und daraus z.B. einen Pointer compiliert, ist das ein Implementierungs-Detail des Compilers. Aber vom C++-Standardpunkt aus gesehen, ist das nicht Sinn von Refs. Sonst hätten es auch gleich Pointer bleiben können. Gehe hier absolut mit camper konform.



  • Hi,

    ich finde, dass sind ja alles schöne philosopische Aussagen über "das Wesen der Referenz als solcher" .... aber in der Praxis verwende ich doch Referenzen an denselben Stellen, wo ich sonst (*const)-Verwenden würde, oder ?
    Und wurden sie nicht genau dafür eingeführt ?

    Mal andersherum:

    ...Sonst hätten es auch gleich Pointer bleiben können.

    Ja ... warum eigentlich nicht ? Wo hat man eigentlich gewonnen dadurch, dass es Referenzen gibt ? Dadurch, dass man jetzt "Tipparbeit spart" ? ("." statt "->" bzw. kein "*" mehr; und bei der Übergabe spart man ein "&")...

    Ich persönlich (da kann ich natürlich falsch liegen) sehe in Referenzen "moderne Pointer": Man kann nach der Typdefinition (und Bindung) komplett vergessen, dass man hier auf ein anderes Objekt referenziert (was z.B. bei templates nett ist, weil da derselbe Code für "A&"'s wie für "A"'s funktioniert)...
    Trotzdem ist für mich die "Aussage" hinter (bzw. die Bedeutung) einer Referenz dieselbe wie hinter einem Pointer: "Eigentlich meine ich nicht Dich, sondern das, worauf Du verweist".

    Gruß,

    Simon2.



  • Referenzen wurden eigeführt, weil Pointer einem bei iostreams nicht weiter helfen, sondern einem hinderlich sind.

    io << var;
    

    Du machst ja nicht das

    io << &var;
    

    Sondern du willst io DAS var-Objekt übergeben. Referenzen sind Namens-Aliase, kannst du im Stroustrup nachlesen (haben sich Camper und ich nicht aus der Nase gezogen!). Wenn du hier im Forum nicht nur Simon2 heißt, sondern auch Andreas2, ist Andreas2 eine Referenz von Simon2.

    int a = 1;
    int &b = a; // b IST a. nicht mehr und nicht weniger. a heißt hier nur b. Nur ein anderer Name.
    
    int *p = &a; // p ist nicht a!!! Sondern ist ein Zeiger der auf a Zeigt.
    

    Es ist ein essenzieller Unterschied zu einem Pointer, weil ein Pointer eine eigenständige Variable ist.

    Noch klarer wird es hier:

    void foo(Hallo &a)
    {
       // a IST zur Laufzeit b!!! Nur ein anderes Namensschild. Physikalisch ist es b!
    }
    
    Hallo b;
    foo(b);
    

    Dessen muß man sich bewusst werden, das Referenzen nichts mit Pointern zu tun haben.

    Artchi

    PS: Eigentlich muß ich dir Pointer garnicht erklären, weißt du selber. 😉



  • Hi,

    danke für Deine Antwort.
    So richtig überzeugt hast Du mich allerdings nicht ... auch nicht durch die Verlängerung des "Referenz-ist-Alias-und-nicht-Zeiger"-Mantras. 😉

    Dass "Zeiger" für den Nutzer als eigene Objekte sichtbar sind, ist für mich eh' nur eine C-Altlast-Eigenschaft, die bei den Referenzen inzwischen durch den Compiler (falls intern überhaupt noch so implementiert) versteckt wird.
    Auch hinter dem Zeiger steht nur die Idee, ein Ding unter einem anderen Namen anfassen zu können (um so den Zugriff zu verallgemeinern) (dass dann irgendjemand die "geniale" Idee hatte, mittels "++" doch einfach mal im Speicher weiterpringen zu können, hat sich IMO nicht bewährt und eigentlich auch nichts mit dem Konzept eines Zeigers zu tun ... 😉 - weswegen man da ja dann iteratoren eingefürt hat).

    Artchi schrieb:

    ...

    io << var;
    

    Du machst ja nicht das

    io << &var;
    

    Wieso sollte Zweiteres konzeptionell etwas Anderes sein als Ersteres ?
    Da man zu Entwurfszeiten der IOStreams bereits Referenzen kannte, hat man sie in der Schnittstellendefinition verwendet und nicht Pointer - aber da sehe ich keine "magic".

    Nochmal deswegen die Frage: Wo und warum verwendest Du an bestimmten Stellen Referenzen und keine (*const) ? (*)

    Gruß,

    Simon2.
    (*) vorgegebene Schnittstellen zählen nicht. 😃



  • Würde das hier gehen, wenn du keine Refs hättest?

    cout << "hallo" << 123 << b << endl;
    

    Deshalb ist es ein konzeptioneller Unterschied, ob du das hier machen müsstest:

    io << &var;
    

    Weil du entweder einen Pointer vom <<-Operator zurück bekommen würdest, oder eine Objektkopie.

    Nochmal deswegen die Frage: Wo und warum verwendest Du an bestimmten Stellen Referenzen und keine (*const) ? (*)

    Habe ich noch nie benutzt. Sehe ich zum ersten mal.
    Das hier

    A* const aPtr = &a;
    

    verstehe ich wiederrum nicht. Was hat das jetzt mit Refs zu tun? aPtr ist doch ein Pointer? Der ist doch was ganz anderes. Halt ein Zeiger. Wo setzt man ihn denn ein?

    Referenzen benutze ich dann, wenn ich mit dem Objekt weiter arbeiten will, das ich vom Benutzer verlange. Ich verlange selten Pointer. Weil ich eigentlich nicht mit der Adresse arbeiten will, die interessiert mich meistens nicht die Bohne. Wenn ich eine Funktion schreibe, in der ich einen String verlange, interessiert mich doch die Adresse des Strings nicht. Ich will DEN String haben. Also benutze ich Refs.

    Artchi

    PS: Tipparbeit-Ersparnis interessiert mich bei Refs nicht (von wegen . statt ->), weil wenn ich in VisualAssist bei Pointern . drücke, VisualAssist autom. ein -> draus macht. 😃



  • Artchi schrieb:

    Würde das hier gehen, wenn du keine Refs hättest?

    cout << "hallo" << 123 << b << endl;
    

    Deshalb ist es ein konzeptioneller Unterschied, ob du das hier machen müsstest:

    ?? warum das ?
    Es gäbe eben die entsprechenden

    ostream* operator<<(ostream*, char const *)
    ostream* operator<<(ostream*, int) und
    ostream* operator<<(ostream*, B const *);
    

    und der Call wäre halt

    cout << "hallo" << 123 << &b << endl;
    

    Artchi schrieb:

    io << &var;
    

    Weil du entweder einen Pointer vom <<-Operator zurück bekommen würdest, oder eine Objektkopie.

    Ja, einen Pointer - und ?
    Wenn wir keine Referenzen hätten, übernähmen die IOFunktionen alle Pointer statt Referenzen... außer dem "syntatkischen Zucker" (s. "spart Tipparbeit"), dass es ohne "*"- und "->" netter aussieht, sehe ich da kein Gegenargument.

    Artchi schrieb:

    ...aPtr ist doch ein Pointer? Der ist doch was ganz anderes. Halt ein Zeiger....

    Simon2 schrieb:

    ... auch nicht durch die Verlängerung des "Referenz-ist-Alias-und-nicht-Zeiger"-Mantras. ;)...

    Artchi schrieb:

    Wo setzt man ihn denn ein?
    ...

    Na da, wo man den Zeiger nicht mehr "umbiegen" will:

    void f(int& i) { i=2; }   // (1)
    void f(int * const i) { *i = 2; } // (2)
    void g(int *i) { ++i; f(i); }  // (3)
    void h(int * const i) { g(i); } // KRACH !!
    
    int main() {
       int iArr[10];
       f(i[2]); // -> (1)
       f(&(i[2])); // -> (2)
       return 0;
    }
    

    Oder

    int main() {
       char const * IrgendwasKonstantes = "Inhalt nicht ändern";
       char const * const GenauDASLiteral = "Inhalt UND Verweis nicht ändern";
    
       cout << IrgendwasKonstantes << " : " << GenauDASLiteral << "\n";
    
       char const * MeinNeuerText = "Hey, das ist Neu";
       IrgendwasKonstantes = MeinNeuerText;
       GenauDASLiteral = MeinNeuerText; // KRACH !!!
       return 0;
    }
    

    (*const) also genau da, wo ich einen "Verweis festnageln" will ... eben, wenn er IMMER auf dasselbe Objekt verweisen soll - Hoppla: Genau, wie eine Referenz. 😉

    Artchi schrieb:

    ...Ich verlange selten Pointer. Weil ich eigentlich nicht mit der Adresse arbeiten will, die interessiert mich meistens nicht die Bohne....

    Ich behaupte mal, dass das Interesse an einer Speicheradresse insgesamt sehr gering und eher selten dei Motivation zur Benutzung eines Pointers ist.
    Wenn, dann geht es eher darum, dass man "Verweise umhängen" können muss.
    Folgendes wäre nämlich ohne Pointer nicht möglich:

    // Einfaches Beispiel:
    class A {
       B const * b;
    public:
       A(B const * initB) : b(initB) {}
       haenge_A_an_neues_B(B const * neuesB) { b = neuesB; }
    };
    
    // oder etwas weniger offensichtlihes Beispiel
    std::vector<A*> v; // das klappt mit Referenzen nicht
    

    Gruß,

    Simon2.



  • Hallo,

    mich würde mal interessieren, wofür Funktionszeiger eigentlich gut sein sollen.

    Danke



  • basti33 schrieb:

    mich würde mal interessieren, wofür Funktionszeiger eigentlich gut sein sollen.

    Sie sind immer dann gut, wenn ein Aufruf weiterdelegiert werden muss, weil die aktuelle Funktion keine Ahnung hat, wie sie mit etwas umgehen soll. Funktionszeuger sind Member-Funktionen recht ähnlich (gehen aber eher in die andere Richtung, daher auch der Begriff "callback").

    Konkretes Beispiel: Ich habe eine Sortier-Funktion, die generisch sein soll, d.h. beliebige Objekte sollen sortiert werden. Das Sortieren ist stets vergleichsbasiert, d.h. die Funktion muss wissen, welches von zwei Objekten a und b das kleinere ist. Da die Funktion generisch ist, weiß die Funktion das aber. Daher übergebe ich der Funktion nicht nur das zu sortierende Array sondern außerdem auch eine Funktion, die das eben weiß:

    template <typename T>
    void sort(std::vector<T> const& array, int (*compare)(T, T))
    {
        // ...
    }
    

    Diese Vergleichsfunktion funktioniert so, dass eine negative Zahl zurückgegeben wird, wenn das erste Objekt kleiner ist, 0, wenn beide gleich sind und sonst eine positive Zahl. Sie könnte für zwei Integer z.B. einfach folgendermaßen aussehen:

    int compare_int (int a, int b)
    {
        return a - b;
    }
    

    Jetzt kann man die 'sort'-Funktion so aufrufen:

    std::vector<int> vec;
    // Vektor füllen ...
    sort(vec, &compare_int);
    


  • Danke für deine Antwort. So ganz verstanden habe ich es aber nicht.

    Konrad Rudolph schrieb:

    Da die Funktion generisch ist, weiß die Funktion das aber.

    Meinst du: Da die Funktion generisch ist, weiß die Funktion das aber nicht?
    Außerdem versteh ich irgendwie nicht, wieso man so was nicht mit überladenen Funktionen löst.

    Danke


Anmelden zum Antworten