Pointer: Wann und wozu?



  • Hallo ihr lieben,

    ich tippe mal darauf, dass diese Frage oft gestellt wird. Undzwar:

    Ich lerne gerade Pointer in C++ und habe die Funktionsweise verstanden. Aber ich frage mich dennoch ständig, wozu man Pointer benötigt? Nun, globale Variablen sind unsauber und sollten vermieden werden, wodurch Pointer erhebliche Vorteile bieten. Aber sind es nur diese? Oder gibt es weitere Gründe?

    Wann sollte ich Pointer einsetzen und wann reichen direkte Variablenzugriffe?

    Ich möchte eben vermeiden, zu Beginn bereits unsauber zu programmieren und stelle daher diese Frage.

    Ich hoffe, ihr habt Verständnis dafür und könnt mir helfen.

    Gruß, trax1988



  • Mit Pointern zeigst du auf die Speicheradresse in der der Inhalt
    einer variable gespeichert ist.

    Wenn du z.B. eine Methode hast, die einen Parameter übergeben bekommt, den
    du verändern möchtest, dann ist dieser Wert nur innerhalb der Funktion gültig,
    außerhalb nicht.
    Das passiert nicht, wenn der übergebene Parameter ein Pointer ist.

    Das ist nur eine der unglaublich vielen Möglichkeiten mit Pointern!



  • trax1988 schrieb:

    Ich lerne gerade Pointer in C++ und habe die Funktionsweise verstanden. Aber ich frage mich dennoch ständig, wozu man Pointer benötigt? Nun, globale Variablen sind unsauber und sollten vermieden werden, wodurch Pointer erhebliche Vorteile bieten.

    Das verstehe ich nicht. Erklär bitte mal, wie Du das meinst.

    trax1988 schrieb:

    Aber sind es nur diese? Oder gibt es weitere Gründe?

    Gründe gibt es viele. Ob das, was du da oben meintest, einer davon ist, weiß ich erst, wenn ich es verstanden habe. 🙂

    trax1988 schrieb:

    Wann sollte ich Pointer einsetzen und wann reichen direkte Variablenzugriffe?

    Wenn Du für etwas keine Zeiger brauchst, solltest Du sie auch nicht benutzen. Ich denke, du hast bisher nur sinnfreien Zeiger-Code gesehen, der verdeutlichen soll, wie Zeiger nur funktionieren aber nicht, wo sie sinnvoll eingesetzt werden können.

    Sinvolle Einsatzmöglichekiten:
    - Du möchtest die Lebensdauer eines bestimmten Objekts selbst kontrollieren. Objekte, die im automatischen Speicher angelegt werden leben quasi nur bis die Ausführung das nächste } erreicht hat. Wenn Dir das nicht reicht, kann man Objekte im Freispeicher anlegen. Zugriff auf solche Objekte gibt es dann per Zeiger.
    - Wenn Du einem Objekt X ein anderes Objekt Y bekannt machen willst, kannst du in X einen Zeiger auf das Y-Objekt speichern.

    Beispiel für die 2 Punkte von oben: Eine verkettete Liste als Datenstruktur.



  • Meide Pointer.

    Wo es nicht anders geht: Bei polymorphen Objekten in einer gemeinsamen Liste. Das kommt im Buch erst spät bei "Vererbung".

    Wo es auch nicht anders geht: Wenn man Speicher anlegen muß, dessen Größe zur Compilezeit noch nicht bekannt ist. Aber naja, da kann man die Pointer gut wegpacken in Klassen wie std::vector und dann muß man sie auch nicht mehr selber anfassen. Innendrin sind sie halt noch.

    Und manchmal, um Dinge auszudrücken wie in einem Stammbaum, wer Papa und Mama sind. Einfach draufzeigern.

    Manchmal vereinfachen Pointer die Programmierung irgendwie.

    //erfundenes Beispiel mit Funktionszeigern
    typedef void KeyHandler();
    
    void moveDown(){
      ++y;
      updateGraphics();
    }
    void moveLeft(){
      --x;
      updateGraphics();
    }
    ...
    KeyHandler keyHandlers[256];
    ...
    keyHandlers['A']=&moveLeft;
    keyHandlers['S']=&moveDown;
    ...
    char key=getch();
    KeyHandler daHandler=keyHandlers[key];
    if(daHandler!=nullptr)
       (*daHandler)();
    //Ok, noch keine dolle Vereinfachung. Könnte mir aber vorstellen, wenn man dem 
    //Benutzer auch noch erlauben will, die Tasten umzulegen, daß es dann beginnt, 
    //so Spaß zu machen.
    


  • @Lybraial

    Nun, dessen war ich mir garnicht bewusst. Danke!

    @Krümelkacker

    1. Nun, ich habe gelesen, dass man globale Variablen immer vermeiden sollte, da sie schlichtweg keinen guten Stil bieten und "öffentlich" sind und somit für jeden zugänglich.

    2. Das heisst, man kann die Lebensdauer nur dann kontrollieren, wenn man diese mit Pointer erzeugt und wieder freigibt? Zweiteres klingt sehr plausibel, danke!



  • @Volkard

    Das würde bedeuten: Wenn ich Objekte erzeuge, von denen mir deren Inhalt klar ist kann ich sie auch ohne Pointer erzeugen. Wenn ich aber zur Lautzeit Objekte (Speicher) freigeben will, der sich auf Objekte bezieht, sollte ich diese mit Pointer erzeugen und später freigeben?



  • trax1988 schrieb:

    @Volkard
    Das würde bedeuten: Wenn ich Objekte erzeuge, von denen mir deren Inhalt klar ist kann ich sie auch ohne Pointer erzeugen. Wenn ich aber zur Lautzeit Objekte (Speicher) freigeben will, der sich auf Objekte bezieht, sollte ich diese mit Pointer erzeugen und später freigeben?

    Ich kann die Bedeutung dieses Satzes nicht deuten. Falls er darauf abzielt, eine Stilregel zu finden, die sagt, Zeiger häufiger zu verwenden, als dringend nötig, dann sage ich mal: Nein.



  • trax1988 schrieb:

    1. Nun, ich habe gelesen, dass man globale Variablen immer vermeiden sollte, da sie schlichtweg keinen guten Stil bieten und "öffentlich" sind und somit für jeden zugänglich.

    Da hast du recht, aber das hat mit Pointern nichts zu tun.



  • Pointer auf Objekte haben u.a. den Vorteil, das die Objekte auf dem Heap liegen. Der ist im alg. größer als der Stack. Das merkt man z.B. dann, wenn man mal rekursiv eine sehr große Menge an Objekt, angelegt auf dem Stack, in eine Liste oder ähnliches packt. Der Nachteil ist, dass man für das 'zerstören' zuständig ist. Dieses kann aber durch die Verwendung von 'smart pointer' entschärft werden.
    Gruß



  • Darf man Fragen mit was du lernst, z.B. was für ein Buch?

    In C++ verwendet man rohe Zeiger/Arrays nur dann, wenn man sie wirklich braucht. Ist dies nicht der Fall, solltest du sog. Container nehmen.
    Ich hab mal noch ein paar Zitate gefunden: (ich denke mal, das geht für die hier zitierten Personen in Ordnung. :))

    ipsec schrieb:

    Die ganze Problematik enthält viele Fallstricke, so dass ich dir rate, rohe Arrays und rohes new und delete am besten gar nicht zu verwenden, sondern auf Klassen der Standardbibliothek (hier z.B. vector ) zurückzugreifen.

    Nexus schrieb:

    Ja, Arrays sind wegen ihrer "Features" recht mühsam. Glücklicherweise behebt std::array fast alle Probleme (Kopiersemantik, Debug-Range-Checks, kein Array-To-Pointer-Decay, Methode für Grösse, ...).
    Ich verwende eigentlich nie mehr rohe Arrays.Die STL (ich zähle std::array mal dazu) hat auf Vieles eine Antwort.

    pumuckl schrieb:

    Einige Dinge, die bei C noch unter "Grundlagen" laufen, sind bei C++ eher "Details für Fortgeschrittene". Das heißt, es kann durchaus vorkommen, dass man schon eine Weile C++ lernt und mit der STL per Du ist, bevor man sich mit low-level Pointerfrickeleien und C-Strings auseinandersetzt, die in C zum täglich Brot gehören und einem in den ersten Monaten in Fleisch und Blut übergehen müssen.

    314159265358979 schrieb:

    Allerdings solltest du rohe Arrays in C++ nicht verwenden, es sei denn, du hast einen guten Grund dafür.



  • Helmut.Jakoby schrieb:

    Pointer auf Objekte haben u.a. den Vorteil, das die Objekte auf dem Heap liegen.

    Du vermischst gerade zwei Konzepte – Zeiger und Speicherverwaltung. Man kann sehr wohl Zeiger haben, ohne Speicher manuell zu verwalten. Das ist auch ein sinnvoller Anwendungszweck von Zeigern. Um Speicherverwaltung wegzukapseln, gibt es RAII (am wichtigsten sind Container und Smart-Pointer).



  • Ich sollte also kein Array von Objekten erzeugen, sondern diese in einem Container erzeugen.

    Ich habe zu den Pointer noch eine Frage: Wenn ich nun eine Klasse definiere und ich z.B. einen... Titel fesetsetzen möchte, so kann die Größe doch variabel sein. Ist es dort sinnvoller einen Pointer zu verwenden oder einfach die Größe beim anlegen durch den Konstruktor festzusetzen?

    EDIT: Bei einem Objekt-Array dann einfach ein Vektor-Container verwenden oder kann man diesen auch typisieren?



  • trax1988 schrieb:

    Ich sollte also kein Array von Objekten erzeugen, sondern diese in einem Container erzeugen.

    Ja.
    Kein Problem, wenn Du dem Buch folgst und anfangs mal Arrays benutzt. Aber spaäter gehts in Richtung std::vector und std::array. Wenn Du das im Hinterkopf hast, ist alles im Lot.

    trax1988 schrieb:

    Ich habe zu den Pointer noch eine Frage: Wenn ich nun eine Klasse definiere und ich z.B. einen... Titel fesetsetzen möchte, so kann die Größe doch variabel sein. Ist es dort sinnvoller einen Pointer zu verwenden oder einfach die Größe beim anlegen durch den Konstruktor festzusetzen?

    Pointer. Also nee, Pointer wäre zwar richtig, aber RAII ist noch wichtiger. Um den Pointer eine Klasse basten, die genau das macht, daß Du nicht mehr selber delete aufrufen mußt. Ach, die gibt's für Titel natürlich auch schon. std::string heißt sie.

    trax1988 schrieb:

    Bei einem Objekt-Array dann einfach ein Vektor-Container verwenden oder kann man diesen auch typisieren?

    vector<DeineKlasse>//gut
    bzw
    vector<DeineKlasse*>//nicht so gut, aber bei Vererbung nötig
    irgendwo gabs für den zweiten Fall sowas wie Pointer-Container, die sind wieder gut.



  • trax1988 schrieb:

    Ich habe zu den Pointer noch eine Frage: Wenn ich nun eine Klasse definiere und ich z.B. einen... Titel fesetsetzen möchte, so kann die Größe doch variabel sein. Ist es dort sinnvoller einen Pointer zu verwenden oder einfach die Größe beim anlegen durch den Konstruktor festzusetzen?

    Wenn ich dich richtig verstehe meinst du sowas:

    Klasse objekt("Titel");
    

    Da nimmst du std::string und damit brauchst du dir keine Gedanken über Länge/Größe zu machen.

    trax1988 schrieb:

    EDIT: Bei einem Objekt-Array dann einfach ein Vector-Container verwenden oder kann man diesen auch typisieren?

    Du musst dem Container schon mitteilen, von welchem Typ er Objekte halten soll.
    Z.B.

    vector<int> ganzzahlen;
    

    edit: Für Zeiger nimmst du dann Smart-Pointer:
    http://www.c-plusplus.net/forum/134971
    http://www.c-plusplus.net/forum/284191?highlight=scopedptr



  • Lybrial schrieb:

    Wenn du z.B. eine Methode hast, die einen Parameter übergeben bekommt, den du verändern möchtest, dann ist dieser Wert nur innerhalb der Funktion gültig, außerhalb nicht.
    Das passiert nicht, wenn der übergebene Parameter ein Pointer ist.

    Nur das man unter C++ hier in der Regel eher Referenzen verwendet (Es sei den der Parameter soll optional sein).



  • Ich danke allen für ihre Antworten. Ihr habt mir damit weitergeholfen. Endlich mal kein Forum, in dem man patzige Antworten zu erwarten hat :D.



  • Pointer haben aber gegenüber Referenzen den Vorteil, dass man nicht ausversehen eine Kopie erzeugen kann.

    Foo& give_foo() { ... }
    Foo myFoo = give_foo(); // Coy-Constructor wird getriggert, unmöglich bei Pointer-Rückgabe.
    


  • Ethon schrieb:

    Pointer haben aber gegenüber Referenzen den Vorteil, dass man nicht ausversehen eine Kopie erzeugen kann.

    Foo& give_foo() { ... }
    Foo myFoo = give_foo(); // Coy-Constructor wird getriggert, unmöglich bei Pointer-Rückgabe.
    

    wenn das die gefahr ist, stimmts woanders nicht.



  • Ethon schrieb:

    Pointer haben aber gegenüber Referenzen den Vorteil, dass man nicht ausversehen eine Kopie erzeugen kann.

    Foo& give_foo() { ... }
    Foo myFoo = give_foo(); // Coy-Constructor wird getriggert, unmöglich bei Pointer-Rückgabe.
    

    Sehe ich als konstruiert an, da man hier ja absichtlich ein neues Objekt anlegt (Zuweisung zu einer Variable eines Typs, nicht einer Referenz). Das kann durchaus beabsichtigt sein. Wenn man keine Kopien will, kann man die Klasse entsprechend designen.

    Ich mag im Gegenzug z.B. Zeigerrückgaben nicht sonderlich, da man nicht sieht, wer für den Speicher verantwortlich ist.



  • asc schrieb:

    Ich mag im Gegenzug z.B. Zeigerrückgaben nicht sonderlich, da man nicht sieht, wer für den Speicher verantwortlich ist.

    Es sei denn, man (oder die benutzte Bibliothek) folgt konsequent der Linie, dass rohe Zeiger nicht besitzend sind.


Anmelden zum Antworten