[C++] Zeiger auf Objekte (?)

Gast #7182562
Hallo,

ich habe folgenden Code gegeben, der die Klassen Bruch und Bruchkopie 
beschreibt:
1
#include<string>
2
#include<memory>
3
#include <iostream>
4

5
class Bruch {
6
public:
7
    // Kopierkonstruktor
8
    Bruch(const Bruch& b) : m_zaehler{ b.m_zaehler }, m_nenner{ b.m_nenner } { }
9
    Bruch() : m_zaehler{ 2 }, m_nenner{ 99 } { }
10
    Bruch(int z, int n) : m_zaehler{ z }, m_nenner{ n } { }
11
    int m_zaehler;
12
    int m_nenner;
13
};
14

15
class BruchKopie {
16
public:
17
    Bruch* m_bruch;
18
    BruchKopie(const Bruch& b) : m_bruch{ new Bruch{ b } } { }
19
    ~BruchKopie() {
20
        delete m_bruch;
21
    }
22
};
23

24
int main() {
25
    Bruch b1{ 2, 5 };
26
    b1.m_zaehler = 123;
27
    std::cout << b1.m_zaehler << std::endl;
28

29
    BruchKopie b5{ b1 };
30

31
    // hier weitere Anweisungen
32
}

Warum funktionieren folgende Anweisungen?
1
    std::cout << (b5.m_bruch)->m_zaehler << std::endl;
2
    std::cout << b5.m_bruch->m_zaehler << std::endl;
3
    (b5.m_bruch)->m_zaehler = 77;
4
    std::cout << (*b5.m_bruch).m_zaehler << std::endl;

b5 ist ein Objekt der Klasse Bruchobjekt und besitzt ein Klassenelement 
namens m_bruch, das einen Zeiger auf eine Bruchklasse beinhaltet.
Daher hätte ich erwartet, daß dieses Klassenelement dereferenziert 
werden muß?
1
   std::cout << (b5.*m_bruch).m_zaehler;
2
   std::cout << b5.(*m_bruch).m_zaehler;
3
   std::cout << b5.*m_bruch.m_zaehler;
Die Anweisungen funktionieren aber nicht (in meiner Verzweiflung habe 
ich alle möglichen Kombinationen durchprobiert, Sternchen hier, 
Sternchen da, bis ich auf etwas funktionierendes traf. Besonders 
zielführend scheint mir das nicht zu sein :-)
Gast
Markus schrieb:

> b5 ist ein Objekt der Klasse Bruchobjekt und besitzt ein Klassenelement
> namens m_bruch, das einen Zeiger auf eine Bruchklasse beinhaltet.
> Daher hätte ich erwartet, daß dieses Klassenelement dereferenziert
> werden muß?

Genau das machst du doch.

[code]
std::cout << (*b5.m_bruch).m_zaehler << std::endl;
              DA
[\code]
Moderator
Markus schrieb:
> Daher hätte ich erwartet, daß dieses Klassenelement dereferenziert
> werden muß?

Das wird in den Beispielen ja auch getan, in den ersten drei Zeilen mit
"->", in der letzten Zeile mit "*".

>    std::cout << (b5.*m_bruch).m_zaehler;

>    std::cout << b5.*m_bruch.m_zaehler;

Der ".*"-Operator tut etwas anderes, siehe

  https://en.cppreference.com/w/cpp/language/pointer

(Abschnitt "Pointers to members")

>    std::cout << b5.(*m_bruch).m_zaehler;

Diese Zeile ist schon syntaktisch falsch.
Markus schrieb:
> Aber b5 ist doch ein 'normales' Objekt, kein Zeiger.
> m_bruch ist ein Zeiger.

m_bruch für sich alleine existiert nicht. Der Zeiger heißt b5.m_bruch, 
und den musst du dereferenzieren, was du hier tust:

Markus schrieb:
> std::cout << (b5.m_bruch)->m_zaehler << std::endl;

Die Klammern kann man dabei weglassen, da . und -> von links nach rechts 
ausgewertet wird, daher funktioniert auch:

> std::cout << b5.m_bruch->m_zaehler << std::endl;

Und das hier ist die etwas umständliche Form des voherigen:

> std::cout << (*b5.m_bruch).m_zaehler << std::endl;

Das funktioniert, weil a->b einfach nur eine Kurzform von (*a).b ist. Da 
der Operator . stärker bindet als * (siehe Operatorrangfolge), wird hier 
nicht b5, sondern b5.m_bruch dereferenziert.

> Warum also nicht b5.*m_bruch.m_zaehler?

Weil .* ein ganz anderer Operator ist. Und b5.(*m_bruch) funktioniert 
nicht. Da müsste ja zuerst ein freistehendes m_bruch dereferenziert 
werden und b5.<das Ergebnis davon> gemacht werden, was nicht dem 
entspricht, was du willst und außerdem in C++ gar nicht möglich ist.
Gast
Rolf M. schrieb:
> m_bruch für sich alleine existiert nicht. Der Zeiger heißt b5.m_bruch,
> und den musst du dereferenzieren, was du hier tust:

Ah, okay.
Ich verstehe zwar nicht warum, werde es aber bei obengenannten 
cppreference.com nochmal nachlesen.
Vielen Dank vorerst.
Gast
Markus schrieb:
> Ich verstehe zwar nicht warum, werde es aber bei obengenannten
> cppreference.com nochmal nachlesen.

Ganz einfach, weil du eine Objektinstanz brauchst - ohne die existieren 
Membervariablen nicht. Ist ja der Zweck von deinem Konstruktor, eben 
diese zu initialisieren.

Kannste dir doch mit verschachtelten structs herleiten das ganze:
1
struct A
2
{
3
  struct B 
4
  {
5
     int tolleZahl;
6
     std::string tollerString;
7
  } myMember;
8
};
9

10
//...
11
int main()
12
{
13
  A a;
14
  a.myMember.tolleZahl = 3;
15
}

Guck's dir am besten im Debugger an, da kannste auch Pointer-Ausdrücke 
ausprobieren.

Allerdings noch am Rand, ich würd abraten davon, bei Klassen Zuweisungen 
zu machen, die tief verschachtelte innere Membervariablen ändern. Ist 
zwar so eine Unart auch bei Java und C#, aber in C++ ne blöde Idee. 
Stichwort Law-of-Demeter.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 4 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.