Quando uso um ponto, seta ou dois pontos duplos para me referir a membros de uma classe em C ++?

243

Vindo de outras línguas derivadas de C (como Java ou C #) para C ++, é em primeiro lugar muito confuso que C ++ tem três maneiras de se referir aos membros de uma classe: a::b, a.b, e a->b. Quando uso qual desses operadores?

(Observação: isso deve ser uma entrada para as Perguntas frequentes sobre C ++ do Stack Overflow . Se você quiser criticar a idéia de fornecer uma FAQ neste formulário, a postagem na meta que iniciou tudo isso seria o lugar para isso. essa pergunta é monitorada na sala de chat do C ++ , onde a ideia das Perguntas frequentes começou em primeiro lugar; portanto, é muito provável que sua resposta seja lida pelos que a tiveram.)

sbi
fonte

Respostas:

248

Os três operadores distintos que o C ++ usa para acessar os membros de uma classe ou objeto de classe, ou seja, dois pontos duplos ::, o ponto .e a seta ->, são usados ​​para três cenários diferentes, sempre bem definidos. Sabendo isto permite-lhe saber imediatamente bastante sobre ae bsó de olhar a::b, a.bou a->b, respectivamente, em qualquer código que você olhar.

  1. a::bé usado apenas se bfor um membro da classe (ou espaço para nome) a. Ou seja, nesse caso a, sempre será o nome de uma classe (ou espaço para nome).

  2. a.bé usado apenas se bfor um membro do objeto (ou referência a um objeto) a. Portanto a.b, asempre será um objeto real (ou uma referência a um objeto) de uma classe.

  3. a->bé, originalmente, uma notação abreviada para (*a).b. No entanto, ->é o único operador de acesso de membro que pode ser sobrecarregado; portanto, se aé um objeto de uma classe que sobrecarrega operator->(tipos comuns são ponteiros e iteradores inteligentes), o significado é o que o designer de classe implementou. Para concluir: With a->b, se afor um ponteiro, bserá um membro do objeto ao qual o ponteiro ase refere. Se, no entanto, afor um objeto de uma classe que sobrecarrega esse operador, a função de operador sobrecarregado operator->()é chamada.


As letras pequenas:

  • Em C ++, tipos declarado como class, structou unionsão considerados "de tipo de classe". Portanto, o acima se refere a todos os três.
  • As referências são, semanticamente, aliases aos objetos, então eu deveria ter adicionado "ou referência a um ponteiro" ao # 3 também. No entanto, achei que isso seria mais confuso do que útil, pois as referências a ponteiros ( T*&) raramente são usadas.
  • Os operadores de ponto e seta podem ser usados ​​para se referir a membros de classe estáticos de um objeto, mesmo que não sejam membros do objeto. (Obrigado a Oli por apontar isso!)
sbi
fonte
10
Possivelmente deve ser esclarecido que .e ->também pode ser usado para acessar estatísticas de classe por meio de um objeto, mesmo que não sejam estritamente "membros do objeto".
Oliver Charlesworth
@ Oli: Isso é realmente verdade. Adicionei-o às letras pequenas, pois acho que não é comum e importante o suficiente para ser listado no texto principal.
13133 sbi
3
Para completar, vale a pena ressaltar que operator*()também pode ser sobrecarregado e que nada força essa sobrecarga a ser consistente operator->()! (Eu não downvote BTW, acabou de chegar aqui através de uma longa sequência de duplicatas)
juanchopanza
@OliCharlesworth, você saberia onde isso está especificado no padrão C ++?
the suine
1
@juanchopanza: Você não pode obter o comportamento de encadeamento ->sobrecarregando operator*e usando ., no entanto. Apenas operator->sobrecargas conseguem isso.
Ben Voigt
36

Sugerindo uma alternativa para o ponto 3 do sbi

a->bé usado apenas se afor um ponteiro. É uma abreviação de (*a).b, o bmembro do objeto que aaponta para. O C ++ possui dois tipos de ponteiros, "regular" e inteligente. Para ponteiros regulares, como A* a, o compilador implementa ->. Para ponteiros inteligentes, como std::shared_ptr<A> a, ->é uma função de membro da classe shared_ptr.

Justificativa: o público-alvo desta FAQ não está escrevendo indicadores inteligentes. Eles não precisam saber ->se realmente é chamado operator->()ou que é o único método de acesso de membro que pode ser sobrecarregado.

MSalters
fonte
4
Independentemente de eu concordar ou não, eu dou isso +1apenas por fornecer uma resposta alternativa.
Sfev
2
Bem, para ser justo, ->também é sobrecarregado para iteradores padrão que qualquer programador C ++ deve encontrar em breve, portanto, dizer que é usado apenas para ponteiros pode ser confuso.
Kiscsirke
@Kiscsirke "programadores comuns de C ++" não precisam escrever tipos inteligentes de ponteiro ou iterador, apenas usá-los. "Desreferências como um ponteiro" se aplicam 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;
}

No exemplo de codificação acima, vemos que:
* Acessando membros (atributos e funções) de uma instância (ou objeto) usando o operador dot ( .)
* Acessando membros (atributos e funções) de um ponteiro para um objeto (ou criado por new) using the pointer operator ( ->)
* Acessando funções de membro estáticas da própria classe sem ter um objeto como identificador usando dois pontos duplos ( ::). [ Nota: você também pode chamar a função de membro estático de uma instância com .ou ->que não é recomendada]

Hu Xixi
fonte
@sbi ha tão mal-humorado, eu sei que é algum tipo de repetição. Eu só quero dar um exemplo explícito para mostrar como usá-los. E onde eu disse ->que só pode ser usado por um ponteiro alocado na pilha por new? Abaixo, o segundo item, acho que realmente deixo claro que ->é para apontar. E antes de votar, é melhor você tentar className::non_static_member_function()com o c ++ 14 sozinho. A referência não é um ponteiro; portanto, ela pode ser usada ., e vou deixar mais claro na minha resposta.
Hu Xixi
0

O operador de ponto é usado em cenários de seleção direta de membros.

print(a.b)

Aqui, estamos acessando b, que é um membro direto de um objeto a. Então, principalmente, aé um objeto e bé um membro (função / variável etc) de a.


O operador de seta é usado em cenários de seleção indireta de membros.

print(a->b)

Aqui, estamos acessando o bque é um membro do objeto, apontado por a. É uma abreviação de (*a).be, portanto, aqui, aé principalmente um ponteiro para um objeto e bé um membro desse objeto.


O operador Double Colon (Scope) é usado em cenários de seleção direta de membros relacionados ao espaço para nome.

print(a::b)

Aqui, estamos acessando o bque é um membro da classe / espaço para nome a. Então, principalmente, aé uma classe / espaço para nome e bé um membro (função / variável etc) de a.

muditrustagii
fonte