¿Cuándo debo utilizar un punto, una flecha o dos puntos dobles para referirme a los miembros de una clase en C++?
Viniendo de otros lenguajes derivados de C (como Java o C#) a C++, al principio resulta muy confuso que C++ tenga tres formas de referirse a los miembros de una clase: a::b
, a.b
y a->b
. ¿Cuándo debo utilizar cuál de estos operadores?
Los tres operadores distintos que C++ usa para acceder a los miembros de una clase u objeto de clase, a saber, los dos puntos dobles ::
, el punto .
y la flecha ->
, se usan para tres escenarios diferentes que siempre están bien definidos. Saber esto le permite saber inmediatamente mucho sobre a
y b
con solo mirar a::b
, a.b
o a->b
, respectivamente, en cualquier código que mire.
a::b
solo se usa sib
es miembro de la clase (o espacio de nombres)a
. Es decir, en este casoa
siempre será el nombre de una clase (o espacio de nombres).a.b
solo se usa sib
es un miembro del objeto (o una referencia a un objeto)a
. Entoncesa.b
, fora
siempre será un objeto real (o una referencia a un objeto) de una clase.a->b
es, originalmente, una notación abreviada para(*a).b
. Sin embargo,->
es el único de los operadores de acceso a miembros que se puede sobrecargar, por lo que sia
es un objeto de una clase que se sobrecargaoperator->
(tales tipos comunes son punteros inteligentes e iteradores), entonces el significado es cualquiera que haya implementado el diseñador de clases. Para concluir: Cona->b
, sia
es un puntero,b
será miembro del objeto al quea
hace referencia el puntero. Sin embargo, sia
es un objeto de una clase que sobrecarga este operador, entoncesoperator->()
se invoca la función del operador sobrecargado.
La letra pequeña:
- En C++, los tipos declarados como
class
,struct
ounion
se consideran "de tipo de clase". Entonces lo anterior se refiere a los tres. - Las referencias son, semánticamente, alias de objetos, por lo que debería haber agregado "o referencia a un puntero" al número 3 también. Sin embargo, pensé que esto sería más confuso que útil, ya que las referencias a punteros (
T*&
) rara vez se utilizan. - Los operadores de punto y flecha se pueden utilizar para hacer referencia a miembros de clase estática de un objeto, aunque no sean miembros del objeto. (¡Gracias a Oli por señalar esto!)
Sugiriendo una alternativa para el punto 3 del OSE
a->b
solo se usa si a
es un puntero. Es una abreviatura de (*a).b
, el b
miembro del objeto a
al que apunta. C++ tiene dos tipos de punteros, punteros "normales" e inteligentes. Para punteros regulares como A* a
, el compilador implementa ->
. Para punteros inteligentes como std::shared_ptr<A> a
, ->
es una función miembro de la clase shared_ptr
.
Justificación: el público objetivo de estas preguntas frecuentes no escribe sugerencias inteligentes. No necesitan saber ->
cómo se llama realmente operator->()
o que es el único método de acceso de miembros que puede sobrecargarse.
El operador de punto se utiliza en escenarios de selección directa de miembros.
print(a.b)
Aquí estamos accediendo b
, que es un miembro directo de un objeto a
. Entonces, principalmente, a
es un objeto y b
es miembro (función/variable, etc.) de a
.
El operador de flecha se utiliza en escenarios de selección de miembros indirectos.
print(a->b)
Aquí, estamos accediendo b
a cuál es un miembro del objeto, al que apunta a
. Es una abreviatura de (*a).b
y por lo tanto aquí, a
es principalmente un puntero a un objeto y b
es miembro de ese objeto.
El operador de dos puntos dobles (alcance) se utiliza en escenarios de selección directa de miembros relacionados con espacios de nombres.
print(a::b)
Aquí, estamos accediendo b
a cuál es miembro de la clase/espacio de nombres a
. Entonces, principalmente, a
es una clase/espacio de nombres y b
es miembro (función/variable, etc.) de 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;
}
En el ejemplo de codificación anterior, vemos que:
* Acceder a miembros (atributos y funciones) desde una instancia (u objeto) usando el operador de punto ( .
)
* Acceder a miembros (atributos y funciones) desde un puntero a un objeto (o creado por new
) usando el operador de puntero ( ->
)
* Accediendo a funciones miembro estáticas desde la clase misma sin tener un objeto como identificador usando dos puntos dobles ( ::
). [ Nota: también puede invocar la función miembro estática desde una instancia con .
o ->
que no se recomienda]