Указатели на члены

Объявления указателей на члены — это особый случай объявлений указателей. Они объявляются с помощью следующей последовательности:

описателькласса storage-class-opt cv-qualifiersopttype-specifierms-modifieroptqualified-name::*cv-qualifiersoptidentifierpm-initializer opt;

  1. Спецификатор объявления:

    • Необязательный спецификатор класса хранения.

    • Необязательные const и volatile описатели.

    • Спецификатор типа: имя типа. Это тип элемента, на который нужно указать, а не класс.

  2. Декларатор:

    • Необязательный модификатор, используемый в системах Microsoft. Дополнительные сведения см. в разделе "Модификаторы для конкретного майкрософт".

    • Полное имя класса, содержащего члены, на которые должен указывать указатель.

    • Оператор ::.

    • Оператор *.

    • Необязательные const и volatile описатели.

    • Идентификатор, задающий имя указателя на член.

  3. Необязательный инициализатор указателя на член:

    • Оператор =.

    • Оператор &.

    • Полное имя класса.

    • Оператор ::.

    • Имя нестатитического члена класса соответствующего типа.

Как обычно, в одном объявлении допускается несколько деклараторов (и любые связанные инициализаторы). Указатель на член может не указывать на статический элемент класса, член ссылочного типа или void.

Указатель на член класса отличается от обычного указателя: он содержит сведения о типе элемента и для класса, к которому принадлежит член. Обычный указатель идентифицирует только один объект в памяти (содержит адрес этого объекта). Указатель на член класса идентифицирует этот член в любом экземпляре класса. В следующем примере объявляется класс Window и несколько указателей на данные-член.

// 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()
{
}

В предыдущем примере pwCaption указатель на любой член класса Window , который имеет тип char*. pwCaption имеет тип char * Window::*. В следующем фрагменте кода объявляются указатели на функции-члены SetCaption и GetCaption.

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

Указатели pfnwGC и pfnwSC указывают на функции GetCaption и SetCaption класса Window соответственно. Код копирует информацию непосредственно в заголовок окна с помощью указателя на член 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';

Разница между .* операторами и ->* операторами (операторами указателя на члены) заключается в том, что .* оператор выбирает членов, заданных объектом или ссылкой на объект, а ->* оператор выбирает элементы через указатель. Дополнительные сведения об этих операторах см. в разделе "Выражения" с операторами указателя на члены.

Результатом операторов указателя на член является тип элемента. В этом случае он выглядит так: char *.

В следующем фрагменте кода функции-члены GetCaption и SetCaption вызываются с использованием указателей на члены.

// 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 );

Ограничения указателей на члены

Адрес статического элемента не является указателем на элемент. Это обычный указатель на один экземпляр статического элемента. Для всех объектов данного класса существует только один экземпляр статического элемента. Это означает, что вы можете использовать обычные операторы адреса (&) и деreference (*).

Указатели на члены и виртуальные функции

Вызов виртуальной функции через функцию указателя на член работает так, как если бы функция была вызвана напрямую. Правильная функция ищется в таблице v-table и вызывается.

Ключ для виртуальных функций, работающих как обычно, вызывает их через указатель на базовый класс. (Дополнительные сведения о виртуальных функциях см. в разделе Виртуальные функции.)

В следующем коде показан вызов виртуальной функции через функцию указателя на член.

// 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