Как удостовериться, что виртуальные вызовы метода распространены полностью к базовому классу?

Виртуальная Земля делает это. Существует также веб-сервис в geocoder.us

14
задан Carl Seleborg 14 August 2009 в 09:28
поделиться

7 ответов

Вы придумали несколько хитроумных способов сделать это, заплатив (как вы сами понимаете) цену раздувания класса и добавления кода, который касается не обязанностей объекта, а недостатков программиста.

Настоящий ответ - не делать этого во время выполнения. Это ошибка программиста, а не ошибка времени выполнения.

Сделайте это во время компиляции: используйте языковую конструкцию, если язык ее поддерживает, или используйте шаблон, который его применяет ( например, , Template Method) или сделайте вашу компиляцию зависимой от прохождения тестов и настройте тесты для ее принудительного применения.

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

22
ответ дан 1 December 2019 в 06:35
поделиться

То, что вы ищете, - это просто шаблон невиртуального интерфейса.

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

Google "Невиртуальный интерфейс" для подробностей.

Изменить : после поиска "Шаблонный метод" Узор ", я вижу, что это ' s другое название для невиртуального интерфейса. Я никогда раньше не слышал, чтобы его называли по имени (я не совсем член фан-клуба GoF). Лично я предпочитаю имя «Невиртуальный интерфейс», потому что само имя фактически описывает, что это за шаблон.

Изменить снова : Вот способ сделать это NVI:

class Container
{
public:
  void PrepareForInsertion(ObjectToInsert* pObject)
  {
    PrepareForInsertionImpl(pObject);

    // If you put some base class implementation code here, then you get
    // the same effect you'd get if the derived class called the base class
    // implementation when it's finished.
    //
    // You can also add implementation code in this function before the call
    // to PrepareForInsertionImpl, if you want.
  }

private:
  virtual void PrepareForInsertionImpl(ObjectToInsert* pObject) = 0;
};

class SpecializedContainer : public Container
{
private:
  virtual void PrepareForInsertionImpl(ObjectToInsert* pObject)
  {
    // Do something and return to the base class implementation.
  }
};
13
ответ дан 1 December 2019 в 06:35
поделиться

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

4
ответ дан 1 December 2019 в 06:35
поделиться

Посмотрите на шаблон метода шаблон метода . (Основная идея состоит в том, что вам больше не нужно вызывать метод базового класса.)

0
ответ дан 1 December 2019 в 06:35
поделиться

Один из выходов - вообще не использовать виртуальные методы, а вместо этого позволить пользователю регистрировать обратные вызовы и вызывать их перед выполнением работы prepareForInsertion. Таким образом становится невозможным совершить эту ошибку, поскольку именно базовый класс обеспечивает выполнение как обратных вызовов, так и нормальной обработки. Вы можете получить множество обратных вызовов, если хотите, чтобы такое поведение выполнялось для многих функций. Если вы действительно так часто используете этот шаблон, вы можете изучить такие инструменты, как AspectJ (или любой другой эквивалент C #), которые могут автоматизировать подобные вещи.

0
ответ дан 1 December 2019 в 06:35
поделиться

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

class Container
{
public:
  void PrepareForInsertion(ObjectToInsert* pObject)
  {
    PrepareForInsertionImpl(pObject);
    doBasePreparing(pObject);
  }

protected:
  virtual void PrepareForInsertionImpl(ObjectToInsert* pObject)
  {
    // nothing to do
  }

private:
  void doBasePreparing(ObjectToInsert* pObject)
  {
    // put here your code from Container::PrepareForInsertionImpl
  }
};
0
ответ дан 1 December 2019 в 06:35
поделиться

Создайте сценарий bash с именем client. sh:

#!/bin/bash

cat someFile

while read FOO; do
        echo $FOO >&3
        if [[ $FOO =~ `printf ".*\x00\x1c.*"` ]]; then
                break
        fi
done

Затем вызовите netcat из вашего основного скрипта следующим образом:

3>&1 nc -c ./client.sh somehost 1234

(вам понадобится версия bash 3 для сопоставления регулярных выражений).

Это предполагает, что сервер отправляет данные по строкам - если не вы ' Придется настроить client.sh так, чтобы он читал и отображал символ за раз.

Придется настроить client.sh так, чтобы он читал и отображал символ за раз.

Придется настроить client.sh так, чтобы он читал и отображал символ за раз.

Большая часть кода будет структурирована вокруг модели while (rdr.Read ()), но если у вас есть «ReadString» или «ReadInnerXml ()» в этом цикле, вы обнаружите, что пропускаете элементы на следующей итерации.

Как SAX основано на событиях, этого никогда не произойдет, так как вы не можете выполнять какие-либо операции, которые заставили бы ваш синтаксический анализатор искать вперед.

Лично я считаю, что Microsoft изобрела идею, что XmlReader лучше с объяснением push / pull модель, но я ее особо не покупаю. Итак, Microsoft думает, что вам не нужно создавать конечный автомат с XmlReader, что для меня не имеет смысла, но в любом случае это только мое мнение.

внутри этого цикла вы обнаружите, что пропускаете элементы на следующей итерации.

Поскольку SAX основан на событиях, этого никогда не произойдет, поскольку вы не можете выполнять какие-либо операции, которые заставили бы ваш синтаксический анализатор искать вперед.

Мое личное мнение таково. что Microsoft изобрела представление о том, что XmlReader лучше с объяснением модели push / pull, но я на самом деле не покупаю это. Итак, Microsoft думает, что вам не нужно создавать конечный автомат с XmlReader, что для меня не имеет смысла, но в любом случае это только мое мнение.

внутри этого цикла вы обнаружите, что пропускаете элементы на следующей итерации.

Поскольку SAX основан на событиях, этого никогда не произойдет, поскольку вы не можете выполнять какие-либо операции, которые заставили бы ваш синтаксический анализатор искать вперед.

Мое личное мнение таково. что Microsoft изобрела представление о том, что XmlReader лучше с объяснением модели push / pull, но я на самом деле не покупаю это. Итак, Microsoft думает, что вам не нужно создавать конечный автомат с XmlReader, что для меня не имеет смысла, но в любом случае это только мое мнение.

Лично я считаю, что Microsoft изобрела представление о том, что XmlReader лучше объясняет модель push / pull, но я на самом деле не верю в это. Итак, Microsoft думает, что вам не нужно создавать конечный автомат с XmlReader, что для меня не имеет смысла, но в любом случае это только мое мнение.

Лично я считаю, что Microsoft изобрела представление о том, что XmlReader лучше объясняет модель push / pull, но я на самом деле не верю в это. Итак, Microsoft думает, что вам не нужно создавать конечный автомат с XmlReader, что для меня не имеет смысла, но в любом случае это только мое мнение.

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

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

Это не означает, что каждый класс вызывает свой прямой базовый класс, он может пропустить уровень (я могу ' t имеют явно определенный конструктор копирования, поэтому компилятор создаст конструктор с общедоступной доступностью, что позволяет возвращать его по значению из реализации base .

  • ] Remember_to_call_base объявлен в разделе protected в base , если он был в частном разделе производном , не будет может ссылаться на него вообще.
  • 6
    ответ дан 1 December 2019 в 06:35
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: