Unterschied zwischen Memberpointer und -objekt



  • Hallo,

    ich habe in der Literatur gesehen, dass Objekte entweder über einen Zeiger oder direkt in die Klasse eingebunden werden. Allerdings verstehe ich nicht ganz wodurch sich die beiden Varianten unterscheiden (Vorteile und Nachteile).
    Könntet ihr mir diesbezüglich weiterhelfen?

    Pointer auf Objekt einbinden:

    [cpp]
    class A {};
    
    class B
    {
     private:
      A* ptr;
    };
    [/cpp]
    

    Objekt selbst einbinden:

    [cpp]
    class A {};
    
    class B
    {
     private:
      A a;
    };
    [/cpp]
    

    Danke!



  • Das ist genau wie bei statischen u. dynamischen Variablen.
    Wenn du A a schreibst liegt der Mist aufm Stack. Schreibst du A* a = new a(); ist nur der A* - also Pointer - in Größe von 4 Byte aufm Stack der restliche Speicher kommt vom Heap.



  • FrEEzE2046 schrieb:

    Das ist genau wie bei statischen u. dynamischen Variablen.
    Wenn du A a schreibst liegt der Mist aufm Stack. Schreibst du A* a = new a(); ist nur der A* - also Pointer - in Größe von 4 Byte aufm Stack der restliche Speicher kommt vom Heap.

    Echt?

    class A {};
    
    class B
    {
     private:
      A a;
    };
    
    B* b = new B;
    

    Wo liegt a?



  • jokester schrieb:

    Wo liegt a?

    Auf'm heap. Aber das ist was ganz anderes.
    Kommt natürlich darauf an wo du das schreibst. Ich ging jetzt von innerhalb einer Funktion aus. Wenn es Bestandteil einer anderen Klasse ist und diese wird dynamisch erzeugt greift meine Aussage ja wieder.
    Und wenn ich sage, dass dann die Klasse auf'm heap liegt, dann schließt das logisch alle Member mit ein.



  • Jetzt glaube ich es zu verstehen.

    Sobald der Speicher mit dem Befehl new angefordert wird landet das Objekt auf dem Heap, ansonsten auf dem Stack.

    [cpp]
    class A {};
    
    class B
    {
     private:
      A* ptr;
      B { ptr = new A; }
    };
    [/cpp]
    

    Herzlichen Dank!



  • class A {};
    
    class B
    {
     private:
      A* ptr;
      B { ptr = new A; }
    };
    

    Korrektur:

    class A {};
    
    class B
    {
     private:
      A* ptr;
      B { ptr = new A; }
      ~B { delete ptr; }
    };
    


  • rickie schrieb:

    Jetzt glaube ich es zu verstehen.

    Sobald der Speicher mit dem Befehl new angefordert wird landet das Objekt auf dem Heap, ansonsten auf dem Stack.

    [cpp]
    class A {};
    
    class B
    {
     private:
      A* ptr;
      B { ptr = new A; }
    };
    [/cpp]
    

    Herzlichen Dank!

    Übrigens wenn du die Möglichkeit hast auf Zeiger zu verzichten, dann solltest du das unbedingt tun. Denn angeforderten Speicher muss man selbstständig freigeben.



  • Hab ich nicht bedacht. Danke!



  • FrEEzE2046 schrieb:

    ...Wenn es Bestandteil einer anderen Klasse ist und diese wird dynamisch erzeugt greift meine Aussage ja wieder.
    Und wenn ich sage, dass dann die Klasse auf'm heap liegt, dann schließt das logisch alle Member mit ein.

    najaaaaaa:

    class A {};
    
    class B
    {
     private:
      A* ptr;
      B(A* a) : ptr(a) {}
    };
    
    int main() {
       A a;
       B* b = new B(&a);
    ...
    

    :p

    Man kann einfach nicht "Pointer = dynamisches Objekterzeugung" schließen.
    Ein Zeiger ist lediglich ein Objektverweis und den kann man auf Heap-/Stack-/Sub-/...Objekte zeigen lassen.

    Wichtiger ist IMHO die Tatsache, dass die Verweise bei Zeigern auch "umgebogen" werden können (im Gegensatz zu Referenzen) und ins Nirvana zeigen können (was man entsprechend berücksichtigen muss).

    Die Aussage

    ghj schrieb:

    ...
    Übrigens wenn du die Möglichkeit hast auf ...[xyz]... zu verzichten, dann solltest du das unbedingt tun. ...

    würde ich in der Variante "[xyz] = manuelle Speicherverwaltung" aber voll und ganz unterstützen.
    Und zwar nicht nur wegen der Möglichkeit, delete zu vergessen, sondern auch, weil man mit der Lebenszeit (also auch der Objekterzeugung mit new oder einem zu frühzeitigen delete) Einiges falsch machen kann. Außerdem geraten gerade Anfänger dabei schnell in die Array-Hölle.... 😉

    Gruß,

    Simon2.



  • rickie schrieb:

    Pointer auf Objekt einbinden:

    [cpp]
    class A {};
    
    class B
    {
     private:
      A* ptr;
    };
    [/cpp]
    

    Objekt selbst einbinden:

    [cpp]
    class A {};
    
    class B
    {
     private:
      A a;
    };
    [/cpp]
    

    Ich würde sogar drei Fälle unterscheiden:

    • A* pa; -- Ein Objekt vom Typ B kennt höchstens ein Objekt vom Typ A. B ist nicht für die Verwaltung von A zuständig (von wegen Löschen u.s.w.).
    • A* pa; -- Ein Objekt vom Typ B hat höchstens ein Objekt vom Typ A. B ist für die Verwaltung von A zuständig. Das erfordert einen eigenen Destruktor und entweder eigene Kopierkonstruktor + Zuweisungsoperator oder explizites Deaktivieren des Kopierens (siehe Dreierregel). Logisch gesehen ist das A-Objekt also ein Datenelement von B. Tatsächlich wird es nur über einen Zeiger erreicht und woanders gespeichert. Das kann praktisch sein, wenn Polymorphie eine Rolle spielt, oder es einen "Leerzustand" geben soll, oder wenn es das Objekt schon gibt und die Verwaltung mit der Erzeugung des B-Objekts übergeben wird. Die Container-Klassen funktionieren ähnlich. Logisch gesehen "enthalten" sie Elemente. Diese werden aber nicht im Objekt selbst gespeichert, sondern wo anders.
    • A a; -- Hier ist das A-Objekt logisch und "physikalisch" ein Datenelement eines B-Objekts. Typischerweise muss man hier keine eigenen Kopierkonstruktoren, Zuweisungsoperatoren und Destruktoren anfertigen. Die Subobjekte werden mit B automatisch zerstört, kopiert und zugewiesen (falls möglich).


  • Ein Zeiger
    (1) muss nicht schon bei Instanziierung (Constructor) gesetzt werden
    (2) kann auch später noch geändert werden und auf andere Objekte verweisen.



  • krümelkacker schrieb:

    ...
    Ich würde sogar drei Fälle unterscheiden:
    ...

    Ich finde diese Aufteilung und Erklärung sehr gut!
    Es gibt allerdings (leider) in der Realität noch mindestens einen weiteren Fall, der mit

    A* pa;
    

    gemeint sein kann: Dahinter kann sich auch ein Array verbergen (also nicht "höchstens ein").
    Auch wenn ich sowas selbst nicht programmieren würde, ist es doch möglich (und nicht mal selten).

    Gruß,

    Simon2.


Anmelden zum Antworten