Kiedy używam kropki, strzałki lub dwukropka, aby odwoływać się do członków klasy w C ++?

243

Pochodzących z innych języków C-pochodzić (jak Java czy C #) do C ++, to na początku bardzo mylące, że C ++ ma trzy sposoby odnoszą się do członków klasy: a::b, a.b, i a->b. Kiedy korzystam z jednego z tych operatorów?

(Uwaga: ma to być wpis do często zadawanych pytań na temat C ++ w programie Stack Overflow . Jeśli chcesz skrytykować pomysł podania w tym formularzu odpowiedzi na najczęściej zadawane pytania, to miejsce na to, które rozpoczęło to wszystko, byłoby odpowiednim miejscem. Odpowiedzi na to pytanie jest monitorowane w czacie C ++ , gdzie pomysł FAQ zaczął się w pierwszej kolejności, więc twoja odpowiedź prawdopodobnie zostanie przeczytana przez tych, którzy wpadli na ten pomysł).

sbi
źródło

Odpowiedzi:

248

Trzy różne operatory używane przez C ++ w celu uzyskania dostępu do elementów klasy lub obiektu klasy, mianowicie podwójny dwukropek ::, kropka .i strzałka ->, są używane w trzech różnych scenariuszach, które są zawsze dobrze zdefiniowane. Wiedząc to pozwala natychmiast wiemy sporo na temat ai bpo prostu patrząc na a::b, a.blub a->b, odpowiednio, w każdym kodzie patrzeć.

  1. a::bjest używany tylko wtedy, gdy bnależy do klasy (lub przestrzeni nazw) a. Oznacza to, że w tym przypadku azawsze będzie to nazwa klasy (lub przestrzeni nazw).

  2. a.bjest używany tylko wtedy, gdy bjest członkiem obiektu (lub odniesieniem do obiektu) a. Tak więc a.b, azawsze będzie to rzeczywisty obiekt (lub odniesienie do obiektu) klasy.

  3. a->bjest pierwotnie skrótem dla (*a).b. Jednak ->jest to jedyny z operatorów dostępowych członkiem które mogą być przeciążone, więc jeśli ato obiekt klasy, która przeciążenia operator->(wspólne takie typy są inteligentne wskaźniki i iteratory), to ich znaczenie jest cokolwiek projektant klasa realizowane. Podsumowując: Jeśli a->b, jeśli ajest wskaźnikiem, bbędzie członkiem obiektu, do którego aodnosi się wskaźnik . Jeśli jednak ajest obiektem klasy, który przeciąża tego operatora, wówczas operator->()wywoływana jest funkcja przeciążonego operatora .


Mały druk:

  • W C ++, typy zadeklarowane jako class, structlub unionsą uważane za „typu klasy”. Powyższe odnosi się do wszystkich trzech z nich.
  • Odniesienia są semantycznie aliasami do obiektów, dlatego też powinienem był dodać „lub odniesienie do wskaźnika” do nr 3. Pomyślałem jednak, że byłoby to bardziej mylące niż pomocne, ponieważ odniesienia do wskaźników ( T*&) są rzadko używane.
  • Operatorów kropek i strzałek można używać do odwoływania się do elementów klasy statycznej z obiektu, nawet jeśli nie są one elementami obiektu. (Podziękowania dla Oli za zwrócenie na to uwagi!)
sbi
źródło
10
Należy wyjaśnić, że może być .i ->może być również używany do statyki klasy dostęp za pośrednictwem obiektu, mimo że nie są one ściśle „członków obiektu”.
Oliver Charlesworth,
@Oli: To prawda. Dodałem go do małego wydruku, ponieważ uważam, że nie jest to dość powszechne i wystarczająco ważne, aby znaleźć się w głównym tekście.
sbi
3
Dla kompletności warto zwrócić uwagę na to, że operator*()może być również przeciążone i że nic nie zmusza do tego przeciążenia operator->()! (Nie głosowałem za BTW, po prostu dotarłem tutaj przez długą sekwencję duplikatów)
juanchopanza
@OliCharlesworth czy wiesz, gdzie jest to określone w standardzie C ++?
świnie
1
@juanchopanza: Jednak nie można uzyskać zachowania łańcuchowego ->przez przeciążenie operator*i użycie .. operator->Dostają to tylko przeciążenia.
Ben Voigt
36

Sugerowanie alternatywy dla punktu 3 Sbi

a->bjest używany tylko wtedy, gdy ajest wskaźnikiem. Jest to skrót od (*a).bThe bczłonkiem obiekt, który awskazuje. C ++ ma dwa rodzaje wskaźników, „zwykłe” i inteligentne. W przypadku zwykłych wskaźników, takich jak A* akompilator, implementuje ->. Dla inteligentnych wskaźników, takich jak std::shared_ptr<A> a, ->jest funkcją klasy shared_ptr.

Uzasadnienie: grupa docelowa tego FAQ nie pisze inteligentnych wskazówek. Nie muszą wiedzieć, ->że naprawdę jest wywoływany operator->()lub że jest to jedyna metoda dostępu do elementu, która może zostać przeciążona.

MSalters
źródło
4
Bez względu na to, czy się z tym zgadzam, czy nie, daję temu +1tylko za alternatywną odpowiedź.
sbi
2
Cóż, uczciwość ->jest również przeciążona dla standardowych iteratorów, które każdy programista C ++ powinien wkrótce spotkać, więc powiedzenie, że jest używane tylko do wskaźników, może być mylące.
Kiscsirke
@Kiscsirke „zwykli programiści C ++” nie muszą pisać inteligentnych typów wskaźników ani iteratorów, tylko ich używają. „Dereferencje jak wskaźnik” dotyczą obu tych elementów.
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;
}

Z powyższego przykładu kodowania widzimy, że:
* Uzyskiwanie dostępu do elementów (atrybutów i funkcji) z instancji (lub obiektu) za pomocą operatora kropki ( .)
* Uzyskiwanie dostępu do elementów (atrybutów i funkcji) ze wskaźnika do obiektu (lub utworzonego przez new) za pomocą operatora wskaźnika ( ->)
* Dostęp do statycznych funkcji składowych z samej klasy bez posiadania obiektu jako uchwytu za pomocą podwójnego dwukropka ( ::). [ Uwaga: można również wywoływać funkcję członka statycznego z instancji z .lub ->która nie jest zalecana]

Hu Xixi
źródło
@sbi tak zrzędliwy ha, wiem, że to jakaś powtórka. Chcę tylko podać wyraźny przykład, aby pokazać, jak z nich korzystać. I gdzie powiedziałem, że ->może być używany tylko przez wskaźnik przydzielony przez stertę new? Poniżej drugi punkt, myślę, że naprawdę wyjaśniam, że ->chodzi o wskaźnik. A zanim przegłosujesz, lepiej spróbuj sam className::non_static_member_function()z c ++ 14. Odwołanie nie jest wskaźnikiem, więc można go używać ., a ja wyjaśnię to w mojej odpowiedzi.
Hu Xixi,
0

Operator kropki jest używany w scenariuszach bezpośredniego wyboru członka.

print(a.b)

Tutaj uzyskujemy dostęp b, który jest bezpośrednim członkiem obiektu a. Przede wszystkim ajest więc przedmiotem i bjest członkiem (funkcją / zmienną itp.) a.


Operator strzałek jest używany w scenariuszach pośredniego wyboru członka.

print(a->b)

Tutaj uzyskujemy dostęp bdo elementu obiektu, na który wskazuje a. Jest to skrót (*a).bi dlatego tutaj ajest przede wszystkim wskaźnikiem do obiektu i bjest członkiem tego obiektu.


Operator podwójnego dwukropka (zakres) jest używany w scenariuszach bezpośredniego wyboru elementu związanych z przestrzenią nazw.

print(a::b)

Tutaj uzyskujemy dostęp, bktóry jest członkiem klasy / przestrzeni nazw a. Więc przede wszystkim ajest klasą / przestrzenią nazw i bjest członkiem (funkcja / zmienna itp.) a.

muditrustagii
źródło