Когда я использую точку, стрелку или двойное двоеточие для ссылки на члены класса в C++?

Исходя из других производных от C языков (таких как Java или C#) и C++, поначалу очень сбивает с толку то, что в C++ есть три способа обращения к членам класса: a::b, a.b, а также a->b, Когда я использую какой из этих операторов?

(Примечание. Предполагается, что это будет вход в FAQ по C++ в Stack Overflow. Если вы хотите критиковать идею предоставления FAQ в этой форме, то публикация в meta, с которой все это началось, будет подходящим местом для этого. этот вопрос отслеживается в чате C++, где идея FAQ возникла в первую очередь, поэтому ваш ответ, скорее всего, будет прочитан теми, кто придумал эту идею.)

2 ответа

Три различных оператора C++ использует для доступа к членам класса или объекта класса, а именно к двойному двоеточию :: точка . и стрелка ->, используются для трех различных сценариев, которые всегда четко определены. Знание этого позволяет сразу узнать достаточно много о a а также b просто глядя на a::b, a.b, или же a->b соответственно в любом коде вы смотрите.

  1. a::b используется только если b является членом класса (или пространства имен) a, То есть в этом случае a всегда будет именем класса (или пространства имен).

  2. a.b используется только если b является членом объекта (или ссылкой на объект) a, Таким образом, для a.b, a всегда будет фактическим объектом (или ссылкой на объект) класса.

  3. a->b изначально это сокращенная запись (*a).b, Тем не мение, -> является единственным оператором доступа члена, который может быть перегружен, так что если a это объект класса, который перегружает operator-> (обычно такие типы являются умными указателями и итераторами), тогда значение имеет то, что реализовал дизайнер классов. Заключить: с a->b, если a это указатель, b будет членом объекта указатель a относится к. Если, однако, a является объектом класса, который перегружает этот оператор, а затем перегруженную операторную функцию operator->() вызывается.


Мелкий шрифт:

  • В C++ типы объявлены как class , struct , или же union считаются "типа класса". Таким образом, вышесказанное относится ко всем трем.
  • Семантически ссылки являются псевдонимами для объектов, поэтому мне следовало бы добавить "или ссылку на указатель" на #3. Тем не менее, я подумал, что это будет более запутанным, чем полезным, так как ссылки на указатели ( T*& ) редко когда-либо использовались.
  • Операторы точка и стрелка могут использоваться для ссылки на статические члены класса из объекта, даже если они не являются членами объекта. (Спасибо Оли за то, что указал на это!)

Предлагая альтернативу для пункта 3 sbi

a->b используется только если a это указатель Это сокращение для (*a).b, b член объекта, который a указывает на. В C++ есть два вида указателей: "обычные" и "умные" указатели. Для обычных указателей, таких как A* a Компилятор реализует ->, Для умных указателей, таких как std::shared_ptr<A> a, -> является функцией-членом класса shared_ptr,

Обоснование: целевая аудитория этого FAQ не пишет умных указателей. Им не нужно знать -> действительно называется operator->() или что это единственный метод доступа к элементу, который может быть перегружен.

Оператор точка используется в сценариях прямого выбора члена.

print(a.b)

Здесь мы получаем доступ b, который является прямым членом объекта a. Итак, прежде всего,a это объект и b является членом (функцией / переменной и т. д.) a.


Оператор стрелки используется в сценариях косвенного выбора члена.

print(a->b)

Здесь мы получаем доступ b который является членом объекта, на который указывает a. Это сокращение от(*a).b и так здесь, a в первую очередь указатель на объект и b является членом этого объекта.


Оператор Double Colon (Scope) используется в сценариях прямого выбора элементов, связанных с пространством имен.

print(a::b)

Здесь мы получаем доступ b который является членом класса / пространства имен a. Итак, в первую очередь, a это класс / пространство имен и b является членом (функцией / переменной и т. д.) a.

#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;
}

Из приведенного выше примера кодирования мы видим, что:
* Доступ к членам (атрибутам и функциям) из экземпляра (или объекта) с помощью оператора точки (.)
* Доступ к членам (атрибутам и функциям) из указателя на объект (или созданныйnew) с помощью оператора указателя (->)
* Доступ к статическим функциям-членам из самого класса без использования объекта в качестве дескриптора с использованием двойного двоеточия (::). [Примечание: вы также можете вызвать статическую функцию-член из экземпляра с. или -> что не рекомендуется]

Другие вопросы по тегам