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

243

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

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

SBI
источник

Ответы:

248

Три различных оператора, которые использует 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*&) редко когда-либо используются.
  • Операторы точка и стрелка могут использоваться для ссылки на статические члены класса из объекта, даже если они не являются членами объекта. (Спасибо Оли за то, что указал на это!)
SBI
источник
10
Возможно, следует пояснить, что .и ->может также использоваться для доступа к статическим классам через объект, даже если они не являются строго «членами объекта».
Оливер Чарльзуорт
@ Оли: Это действительно так. Я добавил его в мелкий шрифт, так как я думаю, что он не является обычным и достаточно важным, чтобы быть перечисленным в основном тексте.
sbi
3
Для полноты, возможно, стоит указать, что operator*()также может быть перегружен, и что ничто не заставляет эту перегрузку соответствовать operator->()! (Я не понизил BTW, просто попал сюда через длинную последовательность дубликатов)
juanchopanza
@OliCharlesworth, вы знаете, где это указано в стандарте C ++?
свиньям
1
@juanchopanza: Вы не можете получить поведение цепочки ->при перегрузке operator*и использовании ., однако. Это operator->получают только перегрузки.
Бен Фойгт
36

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

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

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

MSalters
источник
4
Независимо от того, согласен я с этим или нет, я даю это +1просто для альтернативного ответа.
sbi
2
Честно говоря, ->это также перегружено для стандартных итераторов, с которыми вскоре должен встретиться любой программист на C ++, так что утверждение, что оно используется только для указателей, может сбить с толку.
Kiscsirke
@Kiscsirke "обычные программисты на C ++" не должны писать умные указатели или итераторы, просто используя их. «Разыменование как указатель» относится к обоим.
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;
}

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

Ху Сикси
источник
@ sbi, так раздражительный ха, я знаю, что это какое-то повторение. Я просто хочу дать явный пример, чтобы показать, как их использовать. И где я сказал, ->может быть использован только указатель, который выделен в куче new? Ниже, второй пункт, я думаю, я действительно проясняю, что ->для указателя. И перед тем, как понизить голосование, вам лучше попробовать className::non_static_member_function()c ++ 14 самостоятельно. Ссылка не является указателем, поэтому ее можно использовать ., и я поясню в своем ответе.
Ху Сикси
0

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

print(a.b)

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


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

print(a->b)

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


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

print(a::b)

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

muditrustagii
источник