Столкнулся с интересной проблемой при написании программы на с++, есть класс родитель, есть его наследник, в наследнике я переопределяю какую-то функцию, но хочу вызвать и функцию базового класса, решение очевидное и простое
class A
{
public:
virtual void Func()
{
//do something
}
}
class B : A
{
public:
void Func() override
{
A::Func();
//aditional logic
}
}
Все работает и все классно, только меня смущает дублирование кода я должен вызывать метод базового класса во всех наследниках, а что страшнее теоретически могу забыть этот вызов и будут странные ошибки. Решение тоже пришло быстро, решил сделать вот так
class A
{
public:
virtual void Func() final
{
//do something
FuncInternal();
}
protected:
virtual void FuncInternal() = 0;
}
class B : A
{
void FuncInternal() override
{
//aditional logic
}
}
Теперь всегда отработает сначала метод базового класса, потом наследника, но понял что если уровень наследования будет больше 2 то мне будет нужно вводить FuncInternal1, FuncInternal2 и т.д. И вот тут я завис, как мне решить задачу по избавлению от дублирования вызова базовой функции во всех потомках таким образом, чтоб реализация не отличалась в зависимости от количества наследников.
Думаю что я что-то упускаю из общих знаний ООП, привязка к языку не обязательна.
Спасибо
UPDATE1
Источник проблемы в задумке сделать абстрактный класс-интерфейс, к примеру, IRenderable, и если есть объект который наследуется от такого класса Monster : IRenderable, то я уверен что у Monster будет функция рендер и что при обращению к этой функции будет проведен некий единый набор действий описанных в IRenderable.
Ответ
Поскольку вы пишете, что конкретный язык вам безразличен, вот вам пример с честной рефлексией на C#.
Идея: вызывать «родительскую» имплементацию не нужно, вместо этого каждый из порождённых классов может, если захочет, добавить свою имплементацию метода. Базовый метод вызывает все имплементации по очереди.
Классы выглядят при этом так:
class C1
{
public void F(int x)
{
// вызываем в цикле все имплементации
foreach (var impl in Util.GetAllImplementations
// имплементация в C1
private void FImpl(int x) => Console.WriteLine($"From C1::F({x})");
}
class C2 : C1
{
// тут нету имплементации
}
class C3 : C2
{
// имплементация в C1
private void FImpl(int x) => Console.WriteLine($"From C3::F({x})");
}
Класс-утилита (ужасы рефлексии):
static class Util
{
static public IEnumerable
static Type[] GetArgTypes(Type delegateType)
{
if (delegateType == typeof(Action))
return new Type[0];
if (!delegateType.IsGenericType)
throw new ArgumentException("Expected delegate type");
var typedef = delegateType.GetGenericTypeDefinition();
var isFunc = (typedef.Name.StartsWith("Func`"));
var isAction = (typedef.Name.StartsWith("Action`"));
if ((!isFunc && !isAction) || typedef.Namespace != "System")
throw new ArgumentException("Expected delegate type");
var argTypes = delegateType.GetGenericArguments();
if (isFunc)
Array.Resize(ref argTypes, argTypes.Length - 1);
return argTypes;
}
}
Запускаем, получаем результат:
class Program
{
static void Main(string[] args)
{
new C3().F(1);
}
}
From C3::F(1)
From C1::F(1)
Уверен, что на C++ можно получить такой же результат при помощи какой-нибудь шаблонной магии.
Комментариев нет:
Отправить комментарий