Forum: PC-Programmierung [C++] Zeiger auf Objekte (?)


von Markus (Gast)


Lesenswert?

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 :-)

von MaWin (Gast)


Lesenswert?

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]

von Rolf M. (rmagnus)


Lesenswert?

Markus schrieb:
> Warum funktionieren folgende Anweisungen?

Weil du in allen vieren genau das tust:

> Daher hätte ich erwartet, daß dieses Klassenelement dereferenziert
> werden muß?

von Markus (Gast)


Lesenswert?

Aber b5 ist doch ein 'normales' Objekt, kein Zeiger.
m_bruch ist ein Zeiger.
Warum also nicht b5.*m_bruch.m_zaehler?

von MaWin (Gast)


Lesenswert?

Markus schrieb:
> Warum also nicht b5.*m_bruch.m_zaehler?

Weil die Syntax halt so ist in C.

https://en.cppreference.com/w/c/language/operator_precedence

von MaWin (Gast)


Lesenswert?


von Volker Z. (vzavza)


Lesenswert?

Weil du nicht die Membervariable der Klasse, sondern den gesamten 
Ausdruck (Menbervariable der speziellen Instanz) dereferenzieren willst.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

: Bearbeitet durch Moderator
von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von Markus (Gast)


Lesenswert?

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.

von db8fs (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.