0

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

14

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

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

5 ответ(ов)

0

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

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

0

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

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 {
    // нет необходимости переопределять для получения поведения по умолчанию
};

class foobar2 : public pure_foo {
public:
    // нужно явно указать на переопределение, даже чтобы получить поведение по умолчанию
    virtual int interface();
};

int foobar2::interface()
{
    // foobar - ленив; просто использует умолчательное значение pure_foo
    return pure_foo::interface();
}

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

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

0

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

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

0

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

0

Этот вопрос действительно может вызвать путаницу при изучении объектно-ориентированного программирования (OOD) и C++. Лично у меня в голове постоянно крутился вопрос:

Если мне нужна чисто виртуальная функция с реализацией, то зачем делать ее "чистой" изначально? Почему бы просто не оставить ее "виртуальной" и позволить производным классам как-то переопределять базовую реализацию?

Путаница возникает из-за того, что многие разработчики считают отсутствие тела/реализации основной целью и выгодой определения чисто виртуальной функции. Это не так!

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

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

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

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь