Ponteiros para membros

As declarações dos ponteiros para os membros são casos especiais de declarações do ponteiro. Elas são declaradas usando a seguinte sequência:

storage-class-specifiersoptcv-qualifiersopttype-specifierms-modifieroptqualified-name::*cv-qualifiersoptidentificadorpm-initializeropt;

  1. O especificador de declaração:

    • Um especificador de classe de armazenamento opcional.

    • Opcionais const e volatile especificadores.

    • O especificador de tipo: o nome de um tipo. É o tipo do membro a ser apontado, não a classe.

  2. O declarador:

    • Um modificador opcional específico da Microsoft. Para obter mais informações, consulte Modificadores específicos da Microsoft.

    • O nome qualificado da classe que contém os membros a serem apontados.

    • O operador ::.

    • O operador *.

    • Opcionais const e volatile especificadores.

    • O identificador que nomeia o ponteiro para o membro.

  3. Um inicializador opcional de ponteiro para membro:

    • O operador =.

    • O operador &.

    • O nome qualificado da classe.

    • O operador ::.

    • O nome de um membro não estático da classe de tipo apropriado.

Como sempre, vários declaradores (e quaisquer inicializadores associados) são permitidos em uma única declaração. Um ponteiro para membro pode não apontar para um membro estático da classe, um membro do tipo de referência ou void.

Um ponteiro para um membro de uma classe é diferente de um ponteiro regular: ele tem informações de tipo para o tipo do membro e para a classe à qual o membro pertence. Um ponteiro normal (tem o endereço de) identifica somente um único objeto na memória. Um ponteiro para um membro de uma classe identifica esse membro em qualquer instância da classe. O exemplo a seguir declara uma classe, Window, e alguns ponteiros para os dados de membro.

// pointers_to_members1.cpp
class Window
{
public:
   Window();                               // Default constructor.
   Window( int x1, int y1,                 // Constructor specifying
   int x2, int y2 );                       // Window size.
   bool SetCaption( const char *szTitle ); // Set window caption.
   const char *GetCaption();               // Get window caption.
   char *szWinCaption;                     // Window caption.
};

// Declare a pointer to the data member szWinCaption.
char * Window::* pwCaption = &Window::szWinCaption;
int main()
{
}

No exemplo anterior, pwCaption é um ponteiro para qualquer membro da classe Window que tem o tipo char*. O tipo de pwCaption é char * Window::*. O fragmento de código a seguir declara ponteiros para as funções de membro SetCaption e GetCaption.

const char * (Window::* pfnwGC)() = &Window::GetCaption;
bool (Window::* pfnwSC)( const char * ) = &Window::SetCaption;

Os ponteiros pfnwGC e pfnwSC apontam para GetCaption e SetCaption da classe Window, respectivamente. O código copia informações para a legenda da janela diretamente usando o ponteiro para o membro pwCaption:

Window  wMainWindow;
Window *pwChildWindow = new Window;
char   *szUntitled    = "Untitled -  ";
int     cUntitledLen  = strlen( szUntitled );

strcpy_s( wMainWindow.*pwCaption, cUntitledLen, szUntitled );
(wMainWindow.*pwCaption)[cUntitledLen - 1] = '1';     // same as
// wMainWindow.SzWinCaption [cUntitledLen - 1] = '1';
strcpy_s( pwChildWindow->*pwCaption, cUntitledLen, szUntitled );
(pwChildWindow->*pwCaption)[cUntitledLen - 1] = '2'; // same as
// pwChildWindow->szWinCaption[cUntitledLen - 1] = '2';

A diferença entre os operadores .* e ->* (operadores de ponteiro a membro) é que o operador .* seleciona os membros com um objeto ou uma referência de objeto, enquanto o operador ->* seleciona membros por meio de um ponteiro. Para obter mais informações sobre esses operadores, consulte Expressões com operadores de ponteiro à membro.

O resultado dos operadores de ponteiro à membro é o tipo de membro. Nesse caso, use char *.

O fragmento de código a seguir invoca as funções do membro GetCaption e SetCaption usando ponteiros para os membros:

// Allocate a buffer.
enum {
    sizeOfBuffer = 100
};
char szCaptionBase[sizeOfBuffer];

// Copy the main window caption into the buffer
//  and append " [View 1]".
strcpy_s( szCaptionBase, sizeOfBuffer, (wMainWindow.*pfnwGC)() );
strcat_s( szCaptionBase, sizeOfBuffer, " [View 1]" );
// Set the child window's caption.
(pwChildWindow->*pfnwSC)( szCaptionBase );

Restrições em ponteiros para membros

O endereço de um membro estático não é um ponteiro para um membro. É um ponteiro normal para uma instância do membro estático. Apenas uma instância de um membro estático existe para todos os objetos de uma determinada classe. Isso significa que você pode usar o endereço comum dos operadores (&) e desreferenciar (*).

Ponteiros para membros e funções virtuais

Chamar uma função virtual por meio de uma função de ponteiro para membro funciona como se a função tivesse sido chamada diretamente. A função correta é pesquisada na tabela-v e invocada.

A chave para as funções virtuais funcionarem, como sempre, é chamá-las por meio de um ponteiro para uma classe base. (Para obter mais informações sobre funções virtuais, consulte Funções virtuais.)

O código a seguir mostra como chamar uma função virtual com uma função de ponteiro para membro:

// virtual_functions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

class Base
{
public:
    virtual void Print();
};
void (Base::* bfnPrint)() = &Base::Print;
void Base::Print()
{
    cout << "Print function for class Base" << endl;
}

class Derived : public Base
{
public:
    void Print();  // Print is still a virtual function.
};

void Derived::Print()
{
    cout << "Print function for class Derived" << endl;
}

int main()
{
    Base   *bPtr;
    Base    bObject;
    Derived dObject;
    bPtr = &bObject;    // Set pointer to address of bObject.
    (bPtr->*bfnPrint)();
    bPtr = &dObject;    // Set pointer to address of dObject.
    (bPtr->*bfnPrint)();
}

// Output:
// Print function for class Base
// Print function for class Derived