Обратные вызовы в C++. Виталий Евгеньевич Ткаченко

Читать онлайн.
Название Обратные вызовы в C++
Автор произведения Виталий Евгеньевич Ткаченко
Жанр Программирование
Серия
Издательство Программирование
Год выпуска 2020
isbn



Скачать книгу

target="_blank" rel="nofollow" href="#i_015.png"/>

      Рис. 13. Иерархия наследования классов-исполнителей.

      Итак, будем назначать различные указатели на экземпляры классов и методы-члены, как показано в Листинг 13.

Листинг 13. Настройка указателей на классы и методы

      int main()

      {

        Initiator initiator;

        Executor  executor;

        Executor1 executor1;

        Executor2 executor2;

        Executor3 executor3;

        initiator.setup(&executor, &Executor::callbackHandler1);   // (1)

        initiator.setup(&executor, &Executor::callbackHandler2);   // (2)

        initiator.setup(&executor1, &Executor::callbackHandler1);  // (3)

        initiator.setup(&executor1, &Executor::callbackHandler2);  // (4)

        initiator.setup(&executor2, &Executor::callbackHandler1);  // (5)

        initiator.setup(&executor2, &Executor::callbackHandler2);  // (6)

        //initiator.setup(&executor3, &Executor::callbackHandler1); //Incorrect, base class is ambiguous  // (7)

        //initiator.setup(&executor3, &Executor::callbackHandler2); //Incorrect, base class is ambiguous  // (8)

        initiator.setup((Executor1*)&executor3, &Executor::callbackHandler1);  // (9)

        initiator.setup((Executor1*)&executor3, &Executor::callbackHandler2);  // (10)

        initiator.setup((Executor2*)&executor3, &Executor::callbackHandler1);  // (11)

        initiator.setup((Executor2*)&executor3, &Executor::callbackHandler2);  // (12)

      }

      В строках 1 и 2 все прозрачно: какой метод назначен, такой и будет вызван.

      В строке 3 мы назначаем указатель на метод Executor::callbackHandler1, но поскольку в классе Executor1 он переопределен, будет вызван метод Executor1::callbackHandler1.

      В строке 4 мы назначаем указатель на Executor::callbackHandler2; в классе Executor1 такого метода нет (т.е. он не переопределен), поэтому будет вызван метод базового класса Executor::callbackHandler2.

      В строке 5 мы назначаем указатель на Executor::callbackHandler1; в классе Executor2 метод не переопределен, поэтому будет вызван метод базового класса Executor::callbackHandler2.

      В строке 6 мы назначаем указатель на Executor::callbackHandler2; в классе Executor2 он переопределен, поэтому будет вызван метод Executor2:: callbackHandler2.

      С классом Executor3 ситуация еще интереснее, поскольку он использует множественное наследование6. Мы не можем напрямую назначать указатели на методы базового класса, как это приведено в строках 7 и 8, потому что если взглянуть на иерархию наследования, то можно увидеть, что к базовому классу можно добраться двумя путями – через Executor1 либо через Executor2. Таким образом, компилятор не знает, по какому пути выполнять поиск методов, и выдает ошибку. По указанной причине мы должны явно указать в цепочке наследования класс-предшественник. Если в пути наследования какая-нибудь функция окажется переопределена, то она будет вызвана, в противном случае будет вызвана функция базового класса.

      В строке 9 мы в качестве предшественника указываем класс Executor1 и назначаем указатель на метод callbackHandler1. В Executor1 этот метод переопределен, и он будет вызван. В строке 10 мы назначаем указатель на метод callbackHandler2; в Executor1 этот метод не переопределен, поэтому будет вызван метод базового класса Executor::callbackHandler2. Если мы в качестве предшественника будем



<p>6</p>

Вообще, множественное наследование – неоднозначный механизм, который часто подвергается критике. В большинстве современных языков (например, Java, C#, Ruby и др.) множественное наследование не поддерживается. Тем не менее, в C++ множественное наследование существует, поэтому необходимо рассмотреть и такой случай.