Wann verwende ich einen Punkt, einen Pfeil oder einen Doppelpunkt, um auf Mitglieder einer Klasse in C ++ zu verweisen?

243

Von anderen C-abgeleiteten Sprachen (wie Java oder C #) zu C ++ ist es zunächst sehr verwirrend , dass C ++ drei Möglichkeiten , um Mitglieder einer Klasse zu beziehen hat: a::b, a.b, und a->b. Wann verwende ich welchen dieser Operatoren?

(Hinweis: Dies ist als Eintrag in die C ++ - FAQ von Stack Overflow gedacht . Wenn Sie die Idee kritisieren möchten, eine FAQ in dieser Form bereitzustellen, ist die Veröffentlichung auf Meta, mit der all dies begonnen hat , der richtige Ort dafür. Antworten auf Diese Frage wird im C ++ - Chatroom überwacht, in dem die FAQ-Idee ursprünglich begann. Daher wird Ihre Antwort sehr wahrscheinlich von denjenigen gelesen, die auf die Idee gekommen sind.)

sbi
quelle

Antworten:

248

Die drei unterschiedlichen Operatoren, mit denen C ++ auf die Elemente einer Klasse oder eines Klassenobjekts zugreift, nämlich der Doppelpunkt ::, der Punkt .und der Pfeil ->, werden für drei verschiedene Szenarien verwendet, die immer genau definiert sind. Mit diesem Wissen können Sie sofort wissen sehr viel über aund bnur durch einen Blick auf a::b, a.boder a->bjeweils in jedem Code , den Sie betrachten.

  1. a::bwird nur verwendet, wenn bes sich um ein Mitglied der Klasse (oder des Namespace) handelt a. Das heißt, in diesem Fall aist immer der Name einer Klasse (oder eines Namespace).

  2. a.bwird nur verwendet, wenn bes sich um ein Mitglied des Objekts handelt (oder auf ein Objekt verweist) a. Also für a.b, aeine Klasse wird immer ein tatsächliches Objekt (auf ein Objekt oder eine Referenz) sein.

  3. a->bist ursprünglich eine Kurzschreibweise für (*a).b. Es ->ist jedoch der einzige der Mitgliedszugriffsoperatoren, der überladen werden kann. Wenn aalso ein Objekt einer Klasse überladen ist operator->(häufig sind solche Typen intelligente Zeiger und Iteratoren), ist die Bedeutung unabhängig von der Implementierung des Klassen-Designers. Fazit: Mit a->b, wenn aes sich um einen Zeiger handelt, bwird ein Mitglied des Objekts sein, auf das sich der Zeiger abezieht. Wenn es sich jedoch aum ein Objekt einer Klasse handelt, die diesen Operator überlastet, wird die überladene Operatorfunktion operator->()aufgerufen.


Das Kleingedruckte:

  • In C ++ deklarierten Typen wie class, structoder unionsind „der Klassentyp“ betrachtet. Das Obige bezieht sich also auf alle drei.
  • Verweise sind semantisch Aliase für Objekte, daher hätte ich auch # 3 "oder einen Verweis auf einen Zeiger" hinzufügen sollen. Ich dachte jedoch, dass dies eher verwirrend als hilfreich wäre, da Verweise auf pointers ( T*&) selten verwendet werden.
  • Die Punkt- und Pfeiloperatoren können verwendet werden, um auf statische Klassenmitglieder eines Objekts zu verweisen, obwohl sie keine Mitglieder des Objekts sind. (Danke an Oli für den Hinweis!)
sbi
quelle
10
Es sollte möglicherweise klargestellt werden, dass .und ->kann auch verwendet werden, um über ein Objekt auf Klassenstatik zuzugreifen, obwohl sie nicht ausschließlich "Mitglieder des Objekts" sind.
Oliver Charlesworth
@Oli: Das stimmt ja. Ich habe es dem Kleingedruckten hinzugefügt, da ich denke, dass es nicht häufig und wichtig genug ist, um im Haupttext aufgeführt zu werden.
sbi
3
Der Vollständigkeit halber sei darauf hingewiesen, dass operator*()dies auch überlastet werden kann und dass nichts diese Überlastung zwingt, damit übereinzustimmen operator->()! (Ich habe BTW nicht abgelehnt, bin nur über eine lange Folge von Duplikaten
hierher gekommen
@OliCharlesworth Würdest du wissen, wo das im C ++ Standard festgelegt ist?
das Schwein
1
@juanchopanza: Sie können das Verkettungsverhalten von jedoch nicht ->durch Überladen operator*und Verwenden erreichen .. Das bekommen nur operator->Überladungen.
Ben Voigt
36

Vorschlag einer Alternative für sbi Punkt 3

a->bwird nur verwendet, wenn aes sich um einen Zeiger handelt. Es ist eine Abkürzung für (*a).bdas bMitglied des Objekts, auf das averwiesen wird. C ++ hat zwei Arten von Zeigern, "reguläre" und intelligente Zeiger. Für reguläre Zeiger wie z. B. A* aimplementiert der Compiler ->. Für intelligente Zeiger wie std::shared_ptr<A> a, ->ist eine Elementfunktion der Klasse shared_ptr.

Begründung: Die Zielgruppe dieser FAQ schreibt keine intelligenten Zeiger. Sie müssen nicht wissen , dass es ->wirklich aufgerufen operator->()wird oder dass es die einzige Zugriffsmethode für Mitglieder ist, die überladen werden kann.

MSalters
quelle
4
Egal, ob ich damit einverstanden bin oder nicht, ich gebe dies +1nur, um eine alternative Antwort zu geben.
sbi
2
Um fair zu sein, ->ist es auch für Standard-Iteratoren überlastet, die jeder C ++ - Programmierer bald treffen sollte. Es könnte also verwirrend sein, zu sagen, dass es nur für Zeiger verwendet wird.
Kiscsirke
@Kiscsirke "normale C ++ - Programmierer" müssen keine intelligenten Zeiger- oder Iteratortypen schreiben , sondern nur diese verwenden. "Dereferenzen wie ein Zeiger" gilt für beide.
Caleth
0
#include <iostream>
#include <string>

using namespace std;

class Human {
private:
    int age;

public:
    string name;

    Human(int humanAge, string humanName) 
         : age(humanAge), name(std::move(humanName)) {}

    void DoSomething() {
        cout << age << endl;
    }

    static void DisplayAge(const Human& person) {
        cout << person.age << endl;
    }

    // ...
};

int main() {
    // Usage of Dot(.) 
    Human firstMan(13, "Jim"); // firstMan is an instance of class Human
    cout << firstMan.name << endl; // accessing member attributes
    firstMan.DoSomething(); // accessing member functions

    // Usage of Pointer Operator (->)
    Human* secondMan = new Human(24, "Tom");
    cout << secondMan->name << endl; // accessing member attributes
    secondMan->DoSomething(); // accessing member functions
    cout << (*secondMan).name << endl; // accessing member attributes
    (*secondMan).DoSomething(); // accessing member functions

    // Usage of Double Colon (::)
    Human::DisplayAge(firstMan);
    firstMan.DisplayAge(firstMan); // ok but not recommended
    secondMan->DisplayAge(firstMan); // ok but not recommended

    delete(secondMan);

    return 0;
}

Aus dem obigen Codierungsbeispiel geht
Folgendes hervor : * Zugriff auf Elemente (Attribute und Funktionen) von einer Instanz (oder einem Objekt) mit dem Punktoperator ( .)
* Zugriff auf Elemente (Attribute und Funktionen) von einem Zeiger auf ein Objekt (oder erstellt von new) Verwenden des Zeigeroperators ( ->)
* Zugriff auf statische Elementfunktionen von der Klasse selbst aus, ohne ein Objekt als Handle mit dem Doppelpunkt ( ::) zu haben. [ Hinweis: Sie können auch die statische Elementfunktion von einer Instanz aufrufen mit .oder ->die wird nicht empfohlen]

Hu Xixi
quelle
@sbi so mürrisch ha, ich weiß, es ist eine Art Wiederholung. Ich möchte nur ein explizites Beispiel geben, um zu zeigen, wie man sie benutzt. Und wo ich sagte, ->kann nur von einem Zeiger verwendet werden, der auf dem Heap von zugeordnet ist new? Im Folgenden, dem zweiten Punkt, denke ich, mache ich wirklich deutlich, dass ->es sich um einen Zeiger handelt. Und bevor Sie abstimmen, sollten Sie es besser selbst className::non_static_member_function()mit c ++ 14 versuchen . Referenz ist kein Zeiger, daher kann sie verwendet werden ., und ich werde dies in meiner Antwort klarer machen.
Hu Xixi
0

Der Punktoperator wird in Szenarien mit direkter Elementauswahl verwendet.

print(a.b)

Hier greifen wir zu b, was ein direktes Mitglied eines Objekts ist a. Also ist in erster Linie aein Objekt und bein Mitglied (Funktion / Variable usw.) von a.


Der Pfeiloperator wird in indirekten Elementauswahlszenarien verwendet.

print(a->b)

Hier greifen wir darauf zu, bwelches Mitglied des Objekts ist, auf das von gezeigt wird a. Es ist eine Abkürzung von (*a).bund ist hier in aerster Linie ein Zeiger auf ein Objekt und bein Mitglied dieses Objekts.


Der Doppelpunkt-Operator (Scope) wird in Namespace-bezogenen Szenarien für die direkte Auswahl von Mitgliedern verwendet.

print(a::b)

Hier greifen wir darauf zu, bwelches Mitglied der Klasse / des Namespace ist. aAlso ist es in erster Linie aeine Klasse / ein Namespace und bein Mitglied (Funktion / Variable usw.) von a.

muditrustagii
quelle