Pointer und Reference



  • Hallo zusammen,

    ich habe folgenden Quelltext erstellt:

    #include <cstdlib>
    #include <iostream>
    
    using namespace std;
    
    class Tier {
          public:
                  string name;
    
          public:
    
                 Tier() {}
    
                 Tier (string n) : name (n) {}
    
                 //Methoden
                void datenausgeben() {
                      cout << "name: " << name << endl;
                      }
    
    };
    
    class Saeugetier : public Tier {
          private:
                  float anzahlBeine;
    
          public:
                 Saeugetier() {}
    
                 Saeugetier(float anzahl, string n) : anzahlBeine(anzahl), Tier(n) {};    
    
                 void datenausgeben1() {
                      datenausgeben();
                      cout << anzahlBeine;
                      }   
    };
    
    class Testen {
    
      public:
             //&s beinhaltet den Speicherbereich
             bool pruef (Saeugetier &s) {
    
                  cout << &s << endl;
                  s.name ="Rind";
    
                  //HIER SOLL ERMITTELT WERDEN, OB s BEREITS EXISTIERT?
                  //IST DAS KORREKT?
                  if (&s != 0)
                  return true;
                  }    
    
    };
    
    int main(int argc, char *argv[])
    {
     bool flag = false;
    
     Testen *test = new Testen();
    
     Saeugetier *s1[10];
     s1[0] = new Saeugetier(4,"Hase");
     s1[0]->datenausgeben1();
    
     //WAS PASSIERT HIER GENAU???
     flag = test->pruef(*s1[0]);
      s1[0]->datenausgeben1();
    
        system("PAUSE");
        return EXIT_SUCCESS;
    }
    

    Ich verstehe das mit den Zeigern und Referenzen noch nicht.

    Was übergebe ich denn in der Anweisung

    flag = test->pruef(*s1[0]);
    

    genau?
    Hier wird doch ein Zeiger übergeben? Dieser Zeiger verweißt auf den Speicherbereich von s1[0], oder?

    Erhält dann die Methode

    bool pruef (Saeugetier &s)
    

    nur den Speicherbereich übergeben?

    Ist dass dann immer so, wenn ich mit "*" eine Methode aufrufe, dass dann im Methodenkopf der Adressoperator "&" stehen muss?

    Danke!

    Gruß



  • Kurze Zusammenfassung:

    void foo1(int); //Kopie
    void foo2(int&);//Referenz
    void foo3(int*);//Zeiger
    
    int bar = 6;
    foo1(bar);
    foo2(bar);
    foo3(&bar);
    
    int* pointer = &bar;
    foo1(*pointer);
    foo2(*pointer);
    foo3(pointer);
    
    int& ref = bar;
    foo1(ref);
    foo2(ref);
    foo3(&ref);
    

    MfG, EOutOfResources



  • s1 ist ein Array von Zeigern. Mit s1[0] greifst du auf den ersten Zeiger zu, mit *s1[0] greifst du auf den ersten Zeiger zu und dereferenzierst ihn.

    pruef übernimmt eine Referenz auf ein Saeugetier -Objekt. Das heißt, dass die Referenz in deinem Beispiel auf *s1[0] (was ja ein dereferenzierter Zeiger, also ein völlig normales Objekt, ist) weißt und somit syntaktisch vollkommen in Ordnung ist.

    s1[0]->datenausgeben1() dereferenziert den Zeiger in s1[0] (es ist auch (*s1[0]).datenausgeben1() möglich, wenn die Syntax anschaulicher ist) und ruft die Methode datenausgaben1() auf.

    Referenzen und Zeiger sind nicht das gleiche. Referenzen sind nur ein Alias, Zeiger jedoch beinhalten das komplette Objekt. pruef erhält keinen Speicherbereich, sondern ein Alias, und du übergibst keine Adresse, sondern ein Objekt. Deshalb übergibst du auch nicht s1[0] (Adresse), sondern *s1[0] (Objekt).

    Nichts leichter als das.



  • Danke!

    Wie kann man denn rausfinden ob schon ein Zeiger angelegt wurde?

    So wie ich es mache, funktioniert es leider nicht, da ja &s auf irgendetwas referenzieren muss, oder?

    class Testen {
    
      public:
             //&s beinhaltet den Speicherbereich
             bool pruef (Saeugetier &s) {
                  //WARUM WIRD HIER BEREITS EIN SPEICEHRBEREICH RESERVIERT??
                  cout << "speicherbereich verweist auf:" << &s;
    
                  if (&s == 0) {
                  return true;
                  }
    
                  else 
                  return false;
    
                  }    
    
    };
    
    int main(int argc, char *argv[])
    {
     bool flag = false;
    
     Testen *test = new Testen();
     Saeugetier *s1[10];
    
     for (int i = 0; i < 10; i++) {
    
     flag = test->pruef(*s1[i]);
    
     if (flag == true) {
    
              s1[i] = new Saeugetier();
    
    }
    }
    
        system("PAUSE");
        return EXIT_SUCCESS;
    }
    

    Gruß



  • Tom83 schrieb:

    Wie kann man denn rausfinden ob schon ein Zeiger angelegt wurde?

    Das kann man nicht herausfinden. Du kannst nur testen, ob ein Pointer kein 0-Zeiger ist. Dazu musst du natürlich dein Array erst mal mit 0 initialisieren:

    Saeugetier *s1[10] = {};
    

    Anschließend kannst du dann prüfen:

    bool pruef (Saeugetier *s) {
                  cout << "speicherbereich verweist auf:" << (void*)s;
    
                  if (s == 0) {
    

    Die Funktion wird jetzt natürlich anders aufgerufen:

    flag = test->pruef(s1[i]);
    


  • Danke, genau das wollte ich 🙂

    Allerdings, warum muss ich denn nun anders aufrufen und das Objekt selbst übergeben?

    Gruß



  • Tom83 schrieb:

    Allerdings, warum muss ich denn nun anders aufrufen und das Objekt selbst übergeben?

    Du musst die Funktion anders aufrufen, weil du einen Zeiger auf das Objekt übergibst und diesen auf 0 prüfst.



  • Hallo!

    Und bei "*s1[i]" würde ich dann das Obejekt selbst übergeben?
    Stimmt das? Dann leuchtet mir das ein 🙂

    Gruß



  • Tom83 schrieb:

    Hallo!

    Und bei "*s1[i]" würde ich dann das Obejekt selbst übergeben?
    Stimmt das? Dann leuchtet mir das ein 🙂

    Gruß

    Genau. s1[i] die Adresse, *s1[i] das Objekt.

    Wenn du das Ganze auch noch einer Funktion übergibst, die eine Referenz erwartet, dann wird ein Alias für das Objekt erstellt.



  • Hallo,

    ich hab grad keinen Compiler da.

    Kann ich ein komplettes Array mit Objekten als Argument an eine Methode in einer anderen Klasse übegeben, indem ich die Klammern weglasse?

    Viele Grüße


  • Mod

    Tom83 schrieb:

    Kann ich ein komplettes Array mit Objekten als Argument an eine Methode in einer anderen Klasse übegeben, indem ich die Klammern weglasse?

    Nein. Arrays besitzen keine Kopiersemantik.



  • Tom83 schrieb:

    Hallo,

    ich hab grad keinen Compiler da.

    Kann ich ein komplettes Array mit Objekten als Argument an eine Methode in einer anderen Klasse übegeben, indem ich die Klammern weglasse?

    Viele Grüße

    Aber nur, wenn du den Index auch weglässt.

    In der Funktion würde dann natürlich ein Array erwartet. Weil das Array aber viele Elemente aufweisen kann, wird keine tiefe, sondern eine flache Kopie erstellt, oder genauer: Die Funktion erhält lediglich einen Verweis auf das Array.

    Jetzt wird es vor allem für Anfänger kompliziert: wenn du ein Array an eine Funktion gibst:

    //Änderungen hier am Array betreffen auch das Original ...
    void MyProc(char MyArray[]){...}
    
    int main()
    {
        char MyArray[10];
        MyProc(MyArray);
        return 0;
    }
    

    , kannst du im Funktionskopf das Array auch als Zeiger angeben - denn intern wird das Ganze eh in eine Adresse umgewandelt.

    //Funktioniert auch so
    void MyProc(char*MyArray){...}
    
    int main()
    {
        char MyArray[10];
        MyProc(MyArray);
        return 0;
    }
    

    Das heißt nicht, dass MyArray zu einem Zeiger wird - ein Array an sich ist fix, man kann ihm nichts zuweisen (wohl aber den Elementen):

    //Geht nicht!
    MyArray=OtherArray;
    //Geht doch!
    MyArray[1]=OtherArray[2];
    

    Einem Zeiger kann man was zuweisen:

    //Geht!
    MyPointer=MyArray;
    //Geht auch (wenn MyPointer was zugewiesen wurde)!
    MyPointer[1]=MyArray[2];
    

    Arrays und Zeiger sind nicht ein und dasselbe, auch wenn der Zugriff erschreckend ähnlich erfolgt.

    Im Grunde ist es egal - ich weiß nicht, wie der Compiler reagiert, wenn du versuchst, einem Array in einer Funktion, in der es als Zeiger vertreten ist, etwas zuzuweisen, aber er sollte es abstrafen. Aber für die bessere Lesbarkeit solltest du immer zu [] , wenn du Speicher auf dem Stack, und zu * , wenn du Speicher auf dem Heap erwartest, verwenden.



  • Das wichtigste dazu wurde aber noch nicht gesagt. Übergibt man ein Array an eine Funktion

    void foo(char* arr);
    

    oder

    void bar(char arr[]);
    

    , dann geht die Information über die Länge des Arrays verloren! http://ideone.com/NBqtR

    2 Möglichkeiten gibt es, um das Problem zu lösen:

    1.)Eine Referenz auf das Array übergeben. Dies ist die unleserliche, komplizierte Variante: http://ideone.com/elCPx

    2.)Einen Container deiner Wahl nehmen, der Ersatz für ein Array heißt std::tr1::array (bzw std::array, wenn du unter C++0x bist). Findet sich im Header <array>.
    Verwendung: http://ideone.com/ArtD2

    Wenn du einen dynamischen Container brauchst, dann ein std::vector.

    Variante 2 ist eigentlich immer zu bevorzugen!



  • Hallo!

    Ich habe noch eine Frage:

    Auf der ersten Seite stand ja folgendes (manni) hat das geschrieben:

    Tom83 schrieb:

    Wie kann man denn rausfinden ob schon ein Zeiger angelegt wurde?

    Das kann man nicht herausfinden. Du kannst nur testen, ob ein Pointer kein 0-Zeiger ist. Dazu musst du natürlich dein Array erst mal mit 0 initialisieren:
    C/C++ Code:
    Saeugetier *s1[10] = {};
    C/C++ Code:
    Saeugetier *s1[10] = {};
    C/C++ Code:
    Saeugetier *s1[10] = {};

    Anschließend kannst du dann prüfen:
    C/C++ Code:
    bool pruef (Saeugetier s) {
    cout << "speicherbereich verweist auf:" << (void
    )s;

    if (s == 0) {
    C/C++ Code:
    bool pruef (Saeugetier s) {
    cout << "speicherbereich verweist auf:" << (void
    )s;

    if (s == 0) {
    C/C++ Code:
    bool pruef (Saeugetier s) {
    cout << "speicherbereich verweist auf:" << (void
    )s;

    if (s == 0) {

    Die Funktion wird jetzt natürlich anders aufgerufen:
    C/C++ Code:
    flag = test->pruef(s1[i]);
    C/C++ Code:
    flag = test->pruef(s1[i]);
    C/C++ Code:
    flag = test->pruef(s1[i]);

    Die Methode

    bool pruef (Saeugetier *s)
    

    funktioniert auch so:

    bool pruef (Saeugetier s[])
    

    Warum ist dass denn so? Meine Vermutung: Das Array ist ja an sich eh schon ein Zeiger?
    Wenn ich bool pruef (Saeugetier *s[]) eingebe, dann kommt die Fehlermeldung "canot convert from int* to int**) (Das hat meine Vermutung erzeugt, dass hier das Array von sich aus standardmäßig als Zeiger dient und deshalb nicht extra als Zeiger deklariert werden muss?

    Viele Grüße

    Viele Grüße



  • Ein Array ist kein Zeiger. Allerdings wird der Array-Name in den meisten Situationen implizit in einen Zeiger auf das erste Array-Element umgewandelt.
    (und bei Funktionsparametern sind die Angaben int *a und int a[] tatsächlich äquivalent, allerdings nur in der äußersten Dimension)



  • Dankeschön!

    Was passiert denn eigentlich bei

    cout << "der Speicherbereich verweist auf" << (void*) s
    

    die folgende Zeile bewirkt das gleiche:

    cout << "der Speicherbereich verweist auf" << s
    

    Ich hab zwar was von void - Zeigern gelesen, aber noch nicht so recht verstanden.
    bedeutet (void*) s hier evtl. dass mit dem Objekt s selbst gearbeitet wird, also eine andere Schreibweise für "s"?

    Gruß



  • (void*)s ist ein Cast im C-Stil, also eine Typ-Umwandlung. Das heißt, daß du den struct-Zeiger explizit in einen void-Zeiger (=Zeiger auf unstrukturierten Speicher) umwandelst. Bei der Variante cout << s; hast du ebenfalls eine Typumwandlung, allerdings eine implizite.
    (bei der Struktur macht das keinen Unterschied, aber wenn du stattdessen ein char *s verwendest, unterscheiden sich die Ausgaben)



  • Hi @ all,

    ich habe den Thread durchgelesen und noch eine Frage zu dem ganzen Thema.

    Ich habe folgende Zeilen geschrieben, und bitte euch diese zu kontrollieren ob das alles stimmt (vor allem die Kommentare)

    #include <cstdlib>
    #include <iostream>
    
    using namespace std;
    
     void kopie (int z) {
          cout << "FUNKTION kopie(int z) " <<endl;
          //z hat eine eigene Adresse und ist nicht mit a verbunden
          cout << "Die Adresse von z:" << &z << endl;
          cout << "Der WErt von z: " << z << endl << endl<< endl << endl;
          //eine Zuweisung bringt hier nichts, a bleibt unverändert!
          z = 99;
          }
    
     //Übergabe als Referenz
     void referenz (int &x) {
          cout << "FUNKTION referenz (int &x) " << endl;
          //die Adresse von y entspricht der von a, da mit einem
          //Alias gearbeitet wird????
          cout << "Die Adresse von x: " << &x << "  entspricht der von a, da ein Alias von a" << endl;
          cout << "Der Wert von x:    " << x << endl << endl <<endl << endl;
          //Änderungen würden sich hier direkt auf a auswirken!
    
          }
    
     void pointer (int &y) {
          cout << "FUNKTION pointer (int &y)" << endl;
          //y hat eine eigene Adresse, da ein dereferenzierter Zeiger übergeben wurde
          cout << "Die Adresse von y (&y):" << &y <<endl;
          cout << "Der Wert von  y (y)  :" << y <<endl << endl<< endl << endl;
          //Änderungen würden sich hier direkt auf b auswirken!
    
          }
    
    int main(int argc, char *argv[])
    {
    
        int a = 5;
        int b = 8;
        int *c = &b;
    
        cout << "Die Adresse von a: " << &a << endl;
        cout << "Der Wert von a:    " << a <<endl << endl;
    
        //hier wird die Zahl a übergeben (kein Pointer oder Referenz)???
        referenz(a);
        //hier wird die Zahl a übergeben (kein Point oder Referenz)??
        kopie(a);
    
        cout << "Die Adresse von    b  (&b): " << &b << endl;
        cout << "Der Wert von       b   (b): " << b << endl;
        cout << "Die Adresse von    c  (&c): " << &c << endl;
        cout << "Der Wert von       c   (c): " << c << " (also die Adresse von b)" << endl;
        //der Zeiger *c wird nun dereferenziert
        cout << "Der Wert - verweis c  (*c): " << *c << " (also der Wert von b)" << endl << endl;
        pointer(*c);
    
        ///////////////////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////////////
        //NEUER Quellcodebereich///////////////////////////////////////////////////////////////////
    
        //Beispiel REFERENZEN
        int aa = 6;
        //Was weiße ich hier genau zu?? ??
        //das heißt ja ungegfähr "die Adresse von bb entsprich aa (aber "aa" ist ja der Wert, oder??
        int &bb = aa;
    
        //BEISPIEL ZEIGER
        int cc = 9;
        //Dem Zeiger *dd wird die Adresse von cc zugewiesen
        int *dd = &cc;
    
        int ee = 11;
    
        cout << "bb" << bb << endl;
        //die Referenz bb erhält den Wert von ee ...
        bb = ee; 
        //... da bb direkt auf aa verweist, nimmt aa den Wert von ee an.
        cout << "aa" << aa << endl << endl << endl;
    
        //*dd liefert den dereferenzierten Wert
        cout << "cc" << cc << endl;
        cout << "*dd" << *dd << endl;
    
        //NUN VERWEIST dd AUF DIE ADRESSE VON "ee" 
        dd = &ee;
        cout << "cc erneut" << cc <<endl;
        cout << "*dd erneut" << *dd << endl;
    
        system("PAUSE");
        return EXIT_SUCCESS;
    }
    

    was ich noch nicht so ganz verstehe:

    Was wird bei "void referenz (int &x)" bzw. "void pointer (int &x)" übergeben? Es handelt sich doch in beiden Fällen um Objekte bzw. int - Werte??

    Tschau



  • http://ideone.com/wRmYi
    Wird es dadurch etwas klarer?
    Übrigens schreibt man im Normalfall int& n, nicht int &n, selbiges mit Zeigern.



  • Mario2hoch6 schrieb:

    Was wird bei "void referenz (int &x)" bzw. "void pointer (int &x)" übergeben? Es handelt sich doch in beiden Fällen um Objekte bzw. int - Werte??

    Genau genommen übergibst du an beide Funktionen eine Referenz. Übergabe per Zeiger sieht ein wenig anders aus:

    void pointer (int *y) {
          cout << "FUNKTION pointer (int *y)" << endl;
          //y hat eine eigene Adresse, da ein Zeiger übergeben wurde
          cout << "Die Adresse von y (&y):" << &y <<endl;
          //y enthält die Adresse einer übergebenen Variablen
          cout << "Der Wert von  y (y)  :" << y <<endl<
          //y ist ein Pointer und zeigt auf eine Variable
          cout << "Der Wert hinter y (*y):" << *y << endl<< endl << endl;
          //Änderungen an *y würden sich hier direkt auf b auswirken!
    }
    
    //Aufruf per:
    int a;
    zeiger(&a);
    

Log in to reply