Эзотерические операторы на C++

Какова цель следующих эзотерических операторов С++?

Указатель на член

::*

Привязать указатель к элементу указателем

->*

Привязать указатель к элементу по ссылке

.*

(ссылка)

Ответы

Ответ 1

Указатель на элемент позволяет иметь указатель, относящийся к определенному классу.

Итак, скажем, у вас есть контактный класс с несколькими номерами телефонов.

class contact
{
    phonenumber office;
    phonenumber home;
    phonenumber cell;
};

Идея заключается в том, что если у вас есть алгоритм, который должен использовать номер телефона, но решение о том, какой номер телефона следует делать за пределами алгоритма, указатели на участника решают проблему:

void robocall(phonenumber contact::*number, ...);

Теперь вызывающий объект robocall может решить, какой тип номера звонков использовать:

robocall(&contact::home, ...);    // call home numbers
robocall(&contact::office, ...);  // call office number

.* и ->* вступают в игру, если у вас есть указатель. Итак, внутри robocall вы бы сделали:

contact c = ...;
c.*number;    // gets the appropriate phone number of the object

или

contact *pc = ...;
pc->*number;

Ответ 2

Нет такого оператора, как ::*, и его никогда не было. Я не знаю, где вы его получили.

Что касается ->* и .* - это операторы разыменования для указателей типа указателя на член.

struct S {
  int i;
};

int main() {
  int S::*pi = &S::i; // pointer of pointer-to-member type

  S s;
  S* ps = &s;

  s.*pi = 0; // operator `.*` used
  assert(s.i == 0);

  ps->*pi = 1; // operator `->*` used
  assert(s.i == 1);
}

Что касается каких-то указателей к членам... что говорит ваша любимая книга на С++ по этому вопросу?

Ответ 3

Они относятся к указателям-к-членам и указателям-к-членам-функциям.

struct Foo {
    int a() { return 1; }
    int b() { return 2; }
    int c;
};

int main() {
    Foo f;
    f.c = 3;

    typedef int (Foo::*member_fn)(); // pointer-to-member-function
    typedef int (Foo::*data_member); // pointer-to-member

    member_fn mf = &Foo::a;
    (f.*mf)(); // calls the member function pointed to by mf. returns 1
    mf = &Foo::b;
    (f.*mf)(); // This time, returns 2, since mf points to b
    Foo *fp = &f;
    (fp->*mf)(); // same thing, via pointer to f instead of object/reference f.

    data_member dm = &Foo::c;
    f.*dm; // is 3
    f.*dm = 5;
    f.c;  // is now 5.

    Foo f2; // another instance
    f2.c = 12;
    f2.*dm;  // is 12. Same pointer-to-member, different object.
}

Хотя он может выглядеть как один, ::* не является оператором. Это оператор :: и оператор * operator рядом друг с другом. Чтобы доказать это, не прибегая к чтению стандарта, попробуйте добавить пробелы: :: * компилирует, . * нет, также -> *.

Что они действительно полезны для того же принципа, что и указатели на функции. Вы не использовали бы их, как я уже выше, в ситуации, когда вы могли бы просто вызвать функцию по имени, но вы можете передать их в качестве параметров или вернуть их из функций, сохранить их или выбрать один из нескольких на основе сложной логики.

Если это помогает, я считаю, что синтаксис выбран так, что хотя .* является неделимым одиночным оператором, вы можете себе представить, что *dm "означает" c, член, на который указывает dm. Поэтому, если dm указывает на c, то f.*dm совпадает с f.c.

Ответ 4

Обратите внимание на С++ FAQ Lite раздел по указателям на функции-члены. Найдите конкретные "операторы", о которых вы говорите (в большинстве браузеров Ctrl-F открывает диалог поиска/поиска, который позволяет вам искать текст на веб-странице), и это должно помочь вам лучше понять ситуацию.

Ответ 5

Упрощенный ответ - эти операторы позволяют назвать функции-члены "регулярными" функциями (по крайней мере, они выглядят одинаково с точки зрения конечного пользователя). Пример реального мира - они много используются в различных реализациях обратного вызова.

Ответ 6

Это позволяет вам указывать на функции-члены (и переменные-члены), привязанные к определенному экземпляру класса.

Указатели на функции-члены могут быть полезны для таких вещей, как легкие реализации шаблона состояния. Как правило, в любое время, когда вы хотите изменить поведение объекта с течением времени, не прибегая к отключению всего объекта, вы можете рассмотреть использование указателей для функций-членов.

Указатели на переменные-члены могут использоваться, если вы хотите, например, реализовать общий алгоритм для поиска массива структур для записи, которая имеет конкретное значение для данного поля.