Это плохая практика - создавать дополнительные функции в классе, реализующем абстрактный класс?
Совсем нет!
Предположим, у нас есть следующие классы:
class Fruit
{
public:
virtual void Eat() = 0; // pure virtual functions make the class abstract
}
class Berry : public Fruit
{
public:
void Eat() override
{ /* eat the berry... */ }
}
class Kiwi : public Fruit
{
public:
void Eat() override
{ /* eat the kiwi... */ }
void Peel()
{ /* peel the kiwi... */ }
}
Мы, безусловно, можем Eat()
любой Fruit
, с которым мы сталкиваемся, поэтому имеет смысл, что все Fruit
объекты могут быть съедены.
Вы не станете Peel()
a Berry
до того, как съесть его, поэтому не стоит ожидать, что Berry
может быть Peel()
ed. Это было бы ненужным для пользователей, которые в любом случае обрабатывают объект Berry
.
До Eat()
с Kiwi
вы должны Peel()
сначала, поэтому любой, кто обрабатывает объект Kiwi
, будет ожидать, что функция Peel()
будет доступна, и поэтому Peel()
их Kiwi
прежде чем они Eat()
это.
Теперь предположим, что у пользователя завязаны глаза и ему дан указатель Fruit* someFruit
. Они не знают, является ли это Berry
или Kiwi
, но они в любом случае могут try { Eat(); }
, потому что есть фрукты всегда имеет смысл! Если Fruit
был на самом деле Kiwi
и они не Peel()
, то вначале было бы хорошо, если бы функция Kiwi::Eat()
вызвала исключение (throw "yuk!"
) или корректно обработала это неожиданное использование.
Хотя этот пример действительно написан специально для поедания фруктов, мы обычно можем предположить, что пользователь объекта будет знать, какие функции доступны для этого объекта, просто зная его тип. А когда пользователь не знает тип объекта, то неплохо реализовать некоторую проверку ошибок, чтобы неправильно обрабатывать его функции.