Когда я использую точку, стрелку или двойное двоеточие для ссылки на члены класса в 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
соответственно в любом коде вы смотрите.
a::b
используется только еслиb
является членом класса (или пространства имен)a
, То есть в этом случаеa
всегда будет именем класса (или пространства имен).a.b
используется только еслиb
является членом объекта (или ссылкой на объект)a
, Таким образом, дляa.b
,a
всегда будет фактическим объектом (или ссылкой на объект) класса.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
) с помощью оператора указателя (->
)
* Доступ к статическим функциям-членам из самого класса без использования объекта в качестве дескриптора с использованием двойного двоеточия (::
). [Примечание: вы также можете вызвать статическую функцию-член из экземпляра с.
или ->
что не рекомендуется]