Использованы случаи чистых виртуальных функций с телом?

Недавно я узнал, что в С++ чистые виртуальные функции могут иметь тело.

Каковы реальные возможности использования таких функций?

Ответ 1

Классический - это чистый виртуальный деструктор:

class abstract {
  public: 
    virtual ~abstract() = 0;
};

abstract::~abstract() {}

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

В любом случае, я не помню, чтобы когда-либо действительно была нужна возможность реализовать чистую виртуальную функцию. Для меня это единственная причина, по которой эта особенность заключается в том, что она должна была быть явно запрещена, и Страуструп не видел причин для этого.

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

Ответ 2

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

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

Ответ 3

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

class foo {
public:
    virtual int interface();
};

int foo::interface() 
{
    printf( "default foo::interface() called\n");
    return 0;
};


class pure_foo {
public:
    virtual int interface() = 0;
};

int pure_foo::interface()
{
    printf( "default pure_foo::interface() called\n");
    return 42;
}

//------------------------------------

class foobar : public foo {
    // no need to override to get default behavior
};

class foobar2 : public pure_foo {
public:
    // need to be explicit about the override, even to get default behavior
    virtual int interface();
};

int foobar2::interface()
{
    // foobar is lazy; it'll just use pure_foo default
    return pure_foo::interface();
}

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

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

Ответ 4

Один из вариантов использования - это вызов чистой виртуальной функции из конструктора или деструктора класса.

Ответ 5

Всемогущий Херб Саттер, бывший председатель комитета по стандарту С++, дал 3 сценария, где вы могли бы рассмотреть возможность внедрения реализаций для виртуальных методов.

Должен сказать, что лично - я не считаю никого из них убедительными и вообще считаю, что это один из семантических бородавок С++. Кажется, что С++ выходит из своего пути, чтобы строить и разрывать абстрактные-родительские vtables, чем подвергает их кратко только во время создания/уничтожения детей, а затем эксперты сообщества единодушно рекомендуют никогда не использовать их.

Ответ 6

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

Ответ 7

Этот вопрос может действительно запутать при изучении OOD и С++. Лично одна вещь, которая постоянно приходила мне в голову, была чем-то вроде: Если бы мне понадобилась функция Pure Virtual, которая также имела бы реализацию, то почему ее создание было "чистым" на первом месте? Почему бы просто не оставить его только "Виртуальным" и извлечь выгоду и переопределить базовую реализацию?

Путаница приходит к тому факту, что многие разработчики считают, что ни один орган/реализация не является основной целью/выгодой для определения чистой виртуальной функции. Это неправда! Отсутствие тела в большинстве случаев является логическим следствием наличия чистой виртуальной функции. Основное преимущество наличия чистой виртуальной функции - ОПРЕДЕЛЕНИЕ КОНТРАКТА! Определяя чистую виртуальную функцию, вы хотите, чтобы FORCE каждый полученный, чтобы ВСЕГДА обеспечивали их СОБСТВЕННОЕ ОСУЩЕСТВЛЕНИЕ функции. Этот "Аспект КОНТРАКТА" очень важен, особенно если вы разрабатываете нечто вроде публичного API. Сделать функцию только виртуальной не достаточно, потому что производные больше не вынуждены предоставлять свою собственную реализацию, поэтому вы можете потерять контрактный аспект (это может быть ограничением в случае публичного API). Как обычно сказано:   "Виртуальные функции могут быть отменены, чистые виртуальные функции ДОЛЖНЫ быть переопределены". И в большинстве случаев контракты являются абстрактными понятиями, поэтому для соответствующих чистых виртуальных функций не имеет никакой реализации.

Но иногда и потому, что жизнь странная, вы можете установить сильный контракт между деривативами и также хотеть, чтобы они каким-то образом выиграли от некоторой реализации по умолчанию, указав свое собственное поведение для контракта. Даже если большинство авторов книг рекомендуют избегать попадания в эти ситуации, язык должен был обеспечить систему безопасности, чтобы предотвратить худшее! Простой виртуальной функции будет недостаточно, так как может возникнуть риск избежать контракта. Таким образом, решение С++ предусматривало, что чистые виртуальные функции также смогут обеспечить реализацию по умолчанию.

В цитированной выше статье Саттера приводятся интересные примеры использования функций Pure Virtual с телом.