¿Cuándo uso un punto, una flecha o dos puntos dobles para referirme a los miembros de una clase en C ++?

243

Procedentes de otros lenguajes C-derivados (como Java o C #) para C ++, es al principio muy confuso que C ++ tiene tres formas de referirse a los miembros de una clase: a::b, a.b, y a->b. ¿Cuándo uso cuál de estos operadores?

(Nota: Esto está destinado a ser una entrada a las preguntas frecuentes de C ++ de Stack Overflow . Si desea criticar la idea de proporcionar una pregunta frecuente en este formulario, entonces la publicación en meta que comenzó todo esto sería el lugar para hacerlo. Respuestas a esa pregunta se monitorea en la sala de chat de C ++ , donde la idea de preguntas frecuentes comenzó en primer lugar, por lo que es muy probable que su respuesta sea leída por aquellos que se les ocurrió la idea)

sbi
fuente

Respuestas:

248

Los tres operadores distintos que C ++ usa para acceder a los miembros de una clase u objeto de clase, a saber, el doble ::punto ., el punto y la flecha ->, se usan para tres escenarios diferentes que siempre están bien definidos. Sabiendo esto le permite saber de inmediato mucho sobre ay bcon sólo mirar a::b, a.bo a->b, respectivamente, en cualquier código que mira.

  1. a::bsolo se usa si bes miembro de la clase (o espacio de nombres) a. Es decir, en este caso asiempre será el nombre de una clase (o espacio de nombres).

  2. a.bsolo se usa si bes miembro del objeto (o referencia a un objeto) a. Entonces a.b, asiempre será un objeto real (o una referencia a un objeto) de una clase.

  3. a->bes, originalmente, una notación abreviada para (*a).b. Sin embargo, ->es el único de los operadores de acceso de miembros que se puede sobrecargar, por lo que si aes un objeto de una clase que se sobrecarga operator->(estos tipos comunes son punteros e iteradores inteligentes), entonces el significado es lo que sea que haya implementado el diseñador de la clase. Para concluir: con a->b, si aes un puntero, bserá un miembro del objeto al que se arefiere el puntero . Sin embargo, si aes un objeto de una clase que sobrecarga este operador, entonces operator->()se invoca la función de operador sobrecargado .


La letra pequeña:

  • En C ++, tipos declarados como class, structo unionse consideran "de tipo de clase". Entonces, lo anterior se refiere a los tres.
  • Las referencias son, semánticamente, alias a los objetos, por lo que también debería haber agregado "o referencia a un puntero" al # 3. Sin embargo, pensé que esto sería más confuso que útil, ya que las referencias a punteros ( T*&) rara vez se usan.
  • Los operadores de punto y flecha se pueden usar para referirse a miembros de clase estáticos de un objeto, aunque no sean miembros del objeto. (¡Gracias a Oli por señalar esto!)
sbi
fuente
10
Posiblemente debería aclararse eso .y ->también puede usarse para acceder a estadísticas de clase a través de un objeto, aunque no sean estrictamente "miembros del objeto".
Oliver Charlesworth
@Oli: Eso es cierto. Lo agregué a la letra pequeña, ya que creo que no es lo suficientemente común e importante como para que aparezca en el texto principal.
sbi
3
Para completar, puede valer la pena señalar que operator*()también se puede sobrecargar, ¡y que nada obliga a esa sobrecarga a ser consistente operator->()! (No voté en contra de BTW, solo llegué aquí a través de una larga secuencia de duplicados)
juanchopanza
@OliCharlesworth, ¿sabría dónde se especifica eso en el estándar C ++?
los cerdos
1
@juanchopanza: Sin embargo, no puedes obtener el comportamiento de encadenamiento ->al sobrecargar operator*y usar .. Solo las operator->sobrecargas consiguen eso.
Ben Voigt
36

Sugerir una alternativa para el punto 3 de sbi

a->bsolo se usa si aes un puntero. Es una abreviatura de (*a).b, el bmiembro del objeto que aapunta. C ++ tiene dos tipos de punteros, punteros "regulares" 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 está escribiendo punteros inteligentes. No necesitan saber ->que realmente se llama operator->(), o que es el único método de acceso de miembros que se puede sobrecargar.

MSalters
fuente
44
No importa si estoy de acuerdo o no, doy esto +1solo por proporcionar una respuesta alternativa.
sbi
2
Bueno, para ser justos, ->también está sobrecargado para los iteradores estándar que cualquier programador de C ++ debería cumplir pronto, por lo que decir que solo se usa para punteros podría ser confuso.
Kiscsirke
Los "programadores normales de C ++" de @Kiscsirke no necesitan escribir tipos de puntero o iterador inteligentes, solo usarlos. "Dereferencias como un puntero" se aplica a ambos.
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;
}

En el ejemplo de codificación anterior, vemos que:
* Acceso a miembros (atributos y funciones) desde una instancia (u objeto) utilizando el operador de punto ( .)
* Acceso 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 propia clase sin tener un objeto como asa usando los dos puntos ( ::). [ Nota: también puede invocar la función miembro estática desde una instancia con .o ->que no se recomienda]

Hu Xixi
fuente
@sbi tan gruñón ja, sé que es una especie de repetición. Solo quiero dar un ejemplo explícito para mostrar cómo usarlos. ¿Y donde dije ->que solo puede ser utilizado por un puntero que se asigna en el montón new? A continuación, el segundo elemento, creo que realmente dejo en claro que ->es para el puntero. Y antes de votar a favor, mejor intente className::non_static_member_function()con c ++ 14 usted mismo. La referencia no es un puntero, por lo que puede usarse ., y lo dejaré más claro en mi respuesta.
Hu Xixi,
0

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, aes un objeto y bes un miembro (función / variable, etc.) de a.


El operador de flecha se utiliza en escenarios de selección indirecta de miembros.

print(a->b)

Aquí, estamos accediendo a bcuál es un miembro del objeto, al que señala a. Es una abreviatura de (*a).by, por lo tanto, aquí aes principalmente un puntero a un objeto y bes un miembro de ese objeto.


El operador Double Colon (Scope) se utiliza en escenarios de selección de miembros directos relacionados con el espacio de nombres.

print(a::b)

Aquí, estamos accediendo a bcuál es un miembro de la clase / espacio de nombres a. Entonces, principalmente, aes una clase / espacio de nombres y bes un miembro (función / variable, etc.) de a.

muditrustagii
fuente