Есть среда программирования, которая не в полной мере поддерживает ООП.
В ней можно создавать и наследовать классы, но нету возможности использовать виртуальную функцию.
Если в базовом классе вызвать виртуальную функцию, то даже если она переопределена в классе-наследние, все равно будет вызвана функция базового класса.
Чем можно заменить виртуальные функции в данном случае?
Виртуальные функции очень нужны.
Грубый пример. Есть базовый класс, который читает данные из БД.
Создается несколько наследников, которые переопределяют функцию чтения из БД.
Затем создаются несколько экземпляров этих наследников, указатели на них сохраняются в массиве.
Затем в цикле по массиву нужно вызвать функцию чтения.
Как это сделать без виртуальных функций?
Здравствуйте, zelenprog, Вы писали:
Z>Добрый день!
Z>Есть среда программирования, которая не в полной мере поддерживает ООП. Z>В ней можно создавать и наследовать классы, но нету возможности использовать виртуальную функцию. Z>Если в базовом классе вызвать виртуальную функцию, то даже если она переопределена в классе-наследние, все равно будет вызвана функция базового класса.
Z>Чем можно заменить виртуальные функции в данном случае? Z>Виртуальные функции очень нужны.
Z>Грубый пример. Есть базовый класс, который читает данные из БД. Z>Создается несколько наследников, которые переопределяют функцию чтения из БД. Z>Затем создаются несколько экземпляров этих наследников, указатели на них сохраняются в массиве. Z>Затем в цикле по массиву нужно вызвать функцию чтения. Z>Как это сделать без виртуальных функций?
Здравствуйте, zelenprog, Вы писали:
Z>Как это сделать без виртуальных функций?
Для виртуальных функций нужно чтобы в языке было что-то типа понятия "переменная типа функции" (переменной можно присвоить функцию)
В C/C++ это указатель на функцию, во всяких C# делегаты. Если такой концепции нет, то все, приплыли.
Здравствуйте, zelenprog, Вы писали:
Z>Как это сделать без виртуальных функций?
Так понимаю речь о полиморфизме. Я тут почитал комментарий выше, но некоторые языки даже указатель функции не умеют. В этом случае можно использовать оператор множественного выбора, который не нужно путать с условным оператором, там формируются разные конструкции на уровне кода. Но если и его нет, тогда есть ещё и условный оператор, хотя это похуже будет. Но существуют и другие виды полиморфизма, наоборот более продвинутые, думаю нужно просто выбрать какой использовать.
Для примера Qt генерит вот такой код из mainwindow.cpp в moc_mainwindow.cpp, а ведь это популярная система.
void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
MainWindow *_t = static_cast<MainWindow *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->on_zoom_in_button_clicked(); break;
case 1: _t->on_zoom_out_button_clicked(); break;
default: ;
}
}
Q_UNUSED(_a);
}
Можно хранить ID(который уникален для всех классов) внутри базового класса. Потом по этому id выбирать и вызывать конкретную реализацию, например, простым switch'ом.
Сделать небольшой обработчик скриптов (их на гите миллион) и просто вызывать некую строчку, которая и будет скриптом. А строчку уже каждый дочерний класс по-своему переопределяет. Изврат, конечно, но какая ситуация такое и решение
K>Можно хранить ID(который уникален для всех классов) внутри базового класса. Потом по этому id выбирать и вызывать конкретную реализацию, например, простым switch'ом.
А как по ID найти реализацию?
Где то придется хранить массив созданных объектов-наследников. И искать по ID в этом массиве. Так?
Если виртуальных функций нет, значит они не нужны. Надо писать код так, как задумывалось создателями или менять язык. Изобрести указатель на функцию можно, но не нужно.
Здравствуйте, zelenprog, Вы писали:
Z>Шаблонов тоже нету.
Шаблоны это обобщённое программирование, а ветвление относится к структурному программированию. Полиморфизм можно обеспечить даже на самом простом языке. Выше я уже написал про оператор множественного выбора, в крайнем случае условный оператор.
У меня есть статья Почему программисты прошлого были умнее. Вот ты похоже знаком с высокоуровневыми абстракциями, со всякими таблицами виртуальных методов хотя бы на уровне пользователя. А что можно вызывать одно или другое в зависимости от числа как в операторе множественного выбора или условия как в условном операторе не задумываешься.
Ещё раз вот это оператор множественного выбора.
switch (_id) {
case 0: _t->on_zoom_in_button_clicked(); break;
case 1: _t->on_zoom_out_button_clicked(); break;
default: ;
}
Как видишь здесь нет никаких шаблонов. Условный оператор это if, elseif, else. Чтобы понять различие нужно понять, что он генерирует хотя бы в ассемблере.
В целом же тебе надо разобраться какие конструкции языка у тебя есть, тогда будет понятно, какой вид полиморфного поведения тебе доступен и там уже выбрать архитектуру программы.
Здравствуйте, zelenprog, Вы писали:
Z>Про switch я конечно же знаю. Z>Не понятно откуда базовый класс возьмет ссылки на наследников, чтобы выбрать из них нужного. Z>Вот в чем проблема.
В моём понимании есть.
1. Инструкции — конструкции языка программирования встроенные в компилятор.
2. Архитектура — конструкции составленные из инструкций по определённым самими программистами правилам.
Если у тебя нет переопределения методов, то есть и другие варианты, но это другие виды архитектур. Количество архитектур зависит от количества инструкций, но готовые решения задач которые нужно с помощью них решить взаимозаменяемы.
Мне кажется тема уже почти исчерпана, если только кто-то за тебя не напишет архитектуру программы решающую твою задачу.
class Base {
static constexpr int ID = 1;
Base(int id) : id_(id) {}
int ReadBd() {
switch(id_) {
case Base::ID:
return 11;
case Foo::ID:
return ((Foo*)this)->ReadBd();
}
unreachable();
}
};
class Foo : Base {
static constexpr int ID = 2;
Foo() : Base(ID) {}
int ReadBd() { return 22; }
};
Здравствуйте, koenjihyakkei, Вы писали:
K>Упрощенно на условных плюсах как то так: K>
K> return ((Foo*)this)->ReadBd();
K>};
K>
Здесь предполагается что существует кастинг к базовому классу. Вангую — его тоже нет
В общем я думаю пора колоться, что за язык-то? Что там вообще есть?
Ну наверное проще создать класс который и будет реализовывать эти функции в зависимости от типа объекта.
Через перегрузку методов.
Суть в том, что этот класс будет знать о всех наследниках.
Можно приводить к типу и вызывать метод типа
и солнце б утром не вставало, когда бы не было меня
Да, абстрактных классов и интерфейсов нету.
Но без них можно обойтись, так как строгой типизации нету. Это скриптовый язык.
Будет просто переменная, в которой хранится ссылка (указатель) на объект типа "базовый".
Точнее, эта "переменная" — это this. То есть в методе базового класса через this вызывается виртуальная функция. Нужно чтобы произошел вызов "реальной" переопределенной в потомке функции.
А делегат — это же просто шаблон проектирования. Делегат наверно в моей среде можно сделать.
Здравствуйте, zelenprog, Вы писали:
Z>Да, абстрактных классов и интерфейсов нету. Z>Но без них можно обойтись, так как строгой типизации нету. Это скриптовый язык. Z>Будет просто переменная, в которой хранится ссылка (указатель) на конечный объект.
Z>А делегат — это же просто шаблон проектирования. Делегат наверно в моей среде можно сделать.
Под "делегатом" я имел в виду указатель на функцию, они так во многих языках называются.
Для скриптового языка, если нет делегатов (возможности вызвать функцию через переменную, как ниже например), IMHO — никак.
function foo() {}
function bar() {}
var x;
x = foo;
x()
x = bar;
x()
K>class Base {
K> static constexpr int ID = 1;
K> Base(int id) : id_(id) {}
K> int ReadBd() {
K> switch(id_) {
K> case Base::ID:
K> return 11;
K> case Foo::ID:
K> return ((Foo*)this)->ReadBd();
K> }
K> unreachable();
K> }
K>};
K>class Foo : Base {
K> static constexpr int ID = 2;
K> Foo() : Base(ID) {}
K> int ReadBd() { return 22; }
K>};
K>
Спасибо за идею.
Но вот этого преобразования нету на "моем" языке: "(Foo*)this".
Если в функции базового класса обратиться к this, то всегда получается ссылка на сам объект базового класса.
В этом причина того, что виртуальные функции не работают.
S>Ну наверное проще создать класс который и будет реализовывать эти функции в зависимости от типа объекта. S> Через перегрузку методов. S> Суть в том, что этот класс будет знать о всех наследниках. S> Можно приводить к типу и вызывать метод типа
Не совсем понял.
Этот класс будет хранить ссылки на все объекты в цепочке наследования?
Тогда базовый класс должен иметь доступ к объекту этого класса. Как это сделать?
Не получится ли "зацикливания" ссылок друг на друга?
Здравствуйте, zelenprog, Вы писали:
bnk>>В общем я думаю пора колоться, что за язык-то? Что там вообще есть?
Z>Если я скажу — засмеёте.
Если это скрипт, возможно есть функция типа "EVAL", которая позволяет создать и выполнить произвольный кусок текста в рантайме.
Тогда имя функции можно подставить в виде строки.
bnk>Если это скрипт, возможно есть функция типа "EVAL", которая позволяет создать и выполнить произвольный кусок текста в рантайме. bnk>Тогда имя функции можно подставить в виде строки.
Да, скриптовый язык.
Есть похожая команда, которая выполняет произвольный код в виде текста.
Но я не могу понять, какой код надо выполнить?
Суть в том, чтобы находясь в базовом классе каким-то образом получить ссылку на объект-наследник.
bnk>>Если это скрипт, возможно есть функция типа "EVAL", которая позволяет создать и выполнить произвольный кусок текста в рантайме. bnk>>Тогда имя функции можно подставить в виде строки.
Z>Да, скриптовый язык. Z>Есть похожая команда, которая выполняет произвольный код в виде текста.
Z>Но я не могу понять, какой код надо выполнить? Z>Суть в том, чтобы находясь в базовом классе каким-то образом получить ссылку на объект-наследник.
Ну я имел в виду что-то такое не прокатит:
class Base {
string func_name;
Base() { func_name = "base_foo" }
function base_foo() {}
}
class Child extends Base {
Child() { func_name = "child_foo" }
function child_foo() {}
}
function bar(Base x) {
EVAL("x." + x.func_name + "()")
}
Если и конструкторов тоже нет, можно при инициализации объекта поле задать.
Здравствуйте, zelenprog, Вы писали:
Z>Но вот этого преобразования нету на "моем" языке: "(Foo*)this". Z>Если в функции базового класса обратиться к this, то всегда получается ссылка на сам объект базового класса.
Так, может, тогда все-таки стоит раскрыть что за язык используется?
А то возможно там вообще нет никакой возможности вызвать функцию производного класса, имея указатель на базовый.
Тогда никакого полиморфизма в принципе не получится.
S>>Ну наверное проще создать класс который и будет реализовывать эти функции в зависимости от типа объекта. S>> Через перегрузку методов. S>> Суть в том, что этот класс будет знать о всех наследниках. S>> Можно приводить к типу и вызывать метод типа
Z>Не совсем понял. Z>Этот класс будет хранить ссылки на все объекты в цепочке наследования? Z>Тогда базовый класс должен иметь доступ к объекту этого класса. Как это сделать? Z>Не получится ли "зацикливания" ссылок друг на друга?
Нет он просто будет вызывать методы соответствующего класса.
Или может сам реализовать методы для каждого типа.
А вот базовый класс будет его вызывать метод этого класса передавая this и параметры. А внутри этот класс будет знать, что за тип его вызывает и вызывать соответствующие методы.
и солнце б утром не вставало, когда бы не было меня
В принципе такое прокатит. Но это если вызывать виртуальную функцию извне.
А если нужно вызвать виртуальную функцию из метода базового класса?
Вряд ли в базовом методе this сможет "связаться" с "child_foo()".
Надо попробовать.
Здравствуйте, zelenprog, Вы писали:
bnk>>function bar(Base x) { bnk>> EVAL("x." + x.func_name + "()") bnk>>} bnk>>[/code]
Z>В принципе такое прокатит. Но это если вызывать виртуальную функцию извне. Z>А если нужно вызвать виртуальную функцию из метода базового класса? Z>Вряд ли в базовом методе this сможет "связаться" с "child_foo()". Z>Надо попробовать.
Только не забывай, что eval это всегда тормоза и проблемы с безопасностью.
Здравствуйте, zelenprog, Вы писали:
Z>Есть среда программирования, которая не в полной мере поддерживает ООП.
А что это за среда/язык?
Так можно много чего насоветовать.
Здравствуйте, zelenprog, Вы писали:
Z>Добрый день!
Z>Есть среда программирования, которая не в полной мере поддерживает ООП. Z>В ней можно создавать и наследовать классы, но нету возможности использовать виртуальную функцию. Z>Если в базовом классе вызвать виртуальную функцию, то даже если она переопределена в классе-наследние, все равно будет вызвана функция базового класса.
Z>Чем можно заменить виртуальные функции в данном случае? Z>Виртуальные функции очень нужны.
Z>Грубый пример. Есть базовый класс, который читает данные из БД. Z>Создается несколько наследников, которые переопределяют функцию чтения из БД. Z>Затем создаются несколько экземпляров этих наследников, указатели на них сохраняются в массиве. Z>Затем в цикле по массиву нужно вызвать функцию чтения. Z>Как это сделать без виртуальных функций?
Если это скриптовый язык с динамической типизацией, который позволяет в массив складывать объекты разных типов
то
Попробуйте обойтись без наследования вовсе. Просто отдельные классы, в которых есть метод с одинаковым именем и набором параметров.
Обычные не виртуальные методы.
S>Если это скриптовый язык с динамической типизацией, который позволяет в массив складывать объекты разных типов
Да, в массив можно сложить объекты любых типов.
S>Попробуйте обойтись без наследования вовсе. Просто отдельные классы, в которых есть метод с одинаковым именем и набором параметров. S>Обычные не виртуальные методы.
А как потом этим пользоваться?
У меня задача — из метода базового класса сделать вызов другого метода, который может быть переопределен у потомка.
Я прочитал в интернете, что чисто с архитектурной точки зрения, наследование — это не очень хорошо, так как наследование является сильным связыванием.
Мой скриптовый язык позволяет наследовать классы.
Но как заменить обычные виртуальные функции, другими архитектурными решениями?
S>Кстати, есть ли там статические методы?
Нет, статических методов и статических свойств нету.
S>>Если это скриптовый язык с динамической типизацией, который позволяет в массив складывать объекты разных типов
Z>Да, в массив можно сложить объекты любых типов.
Этот язык позволяет пройтись по такому массиву и вызвать для каждого элемента одноимённые методы?
Z>У меня задача — из метода базового класса сделать вызов другого метода, который может быть переопределен у потомка.
Z>Я прочитал в интернете, что чисто с архитектурной точки зрения, наследование — это не очень хорошо, так как наследование является сильным связыванием. Z>Мой скриптовый язык позволяет наследовать классы. Z>Но как заменить обычные виртуальные функции, другими архитектурными решениями?
Есть такая идиома pimpl
Вы можете сделать внешний класс (обёртка), который хранит в своём свойстве экземпляр другого класса (реализация).
Делаете разные классы реализаций, которые друг с другом не связаны. Но чтобы они все имели схожие наборы методов.
Из метода обёртки можно будет вызывать методы реализаций.
class base_1
{
connect() {}
close() {}
}
class base_2
{
connect() {}
close() {}
}
class wrap
{
var impl
do_something()
{
impl.connect()
impl.close()
}
}
var items = array( new wrap(new base_1), new wrap(new base_2) )
items each it { it.do_something() }
По сути это обычная инкапсуляция. Зато не требует наследования.
Но если у вас уже чётко прописана иерархия классов, на которую вы повлиять не можете — то думаю это вам не поможет)
Кстати если из метода класса реализации нужен метод внешней обёртки, то можно попробовать передать обёртку (this) в метод реализации в качестве параметра.
S>>>Если это скриптовый язык с динамической типизацией, который позволяет в массив складывать объекты разных типов
Z>>Да, в массив можно сложить объекты любых типов.
S>Этот язык позволяет пройтись по такому массиву и вызвать для каждого элемента одноимённые методы?
Да, это возможно.
По массиву можно прийтись и вызвать одинаковый метод для разных объектов, даже если это объекты разного типа (разных классов).
S>Есть такая идиома pimpl S>Вы можете сделать внешний класс (обёртка), который хранит в своём свойстве экземпляр другого класса (реализация). S>Делаете разные классы реализаций, которые друг с другом не связаны. Но чтобы они все имели схожие наборы методов. S>Из метода обёртки можно будет вызывать методы реализаций. S>По сути это обычная инкапсуляция. Зато не требует наследования.
Спасибо, идею понял. Попробую.
S>Но если у вас уже чётко прописана иерархия классов, на которую вы повлиять не можете — то думаю это вам не поможет)
Иерархия классов — это моя разработка. Я сам создавал все классы, начиная от базового.
В базовом(-ых) классе(-ах) задан некий универсальный алгоритм, который вызывает абстрактные (виртуальные) функции.
Эти "абстрактные" функции должны быть реализованы реальными виртуальными функциями в классах-наследниках. На C#, например, никакой проблемы с этим не было.
А вот на моем "новом" скриптовом языке я столкнулся с тем, что это невозможно сделать.
S>Кстати если из метода класса реализации нужен метод внешней обёртки, то можно попробовать передать обёртку (this) в метод реализации в качестве параметра.