Как вызвать функцию динамически с аргументом полиморфизма
Как я могу динамически вызывать функцию вида:childA.function(childB)
в то время как их статические типы у обоих родителей?
и с более подробной информацией:
У меня есть физический проект, где мне нужно рассчитать потенциал 2 молекул. Но у меня есть 2 типа молекул, LC и Col, и у каждого типа есть свои параметры и прочее, однако я хочу иметь возможность динамически вызывать потенциал каждой молекулы.
поэтому я попробовал это:
#include <iostream>
#include <typeinfo>
#include <stdio.h>
using namespace std;
class Col;
class LC;
class Molecule
{
public:
/// more data and functions should be here regarding the molecule
double someBulshit;
virtual double potential(const Molecule * mol){}
virtual double potential(const Col * mol){}
virtual double potential(const LC * mol){}
};
class LC : public Molecule
{
public:
/// more data and functions should be here regarding the LC molecule
virtual double potential(const Molecule * mol) {return 1;}
virtual double potential(const LC * mol) {return 2;}
virtual double potential(const Col * mol) {return 3;}
};
class Col : public Molecule
{
public:
/// more data and function should be here regarding the Col molecule
virtual double potential(const Molecule * mol) {return 4;}
virtual double potential(const LC * mol) {return 5;}
virtual double potential(const Col * mol) {return 6;}
};
int main(int argc, char* argv[])
{
Molecule * mol1 = new Col();
Molecule * mol2 = new LC();
double my_potential = mol1->potential(mol2);
printf ("%f",my_potential);
}
Но я получаю результат 4, поскольку получается, что функция вызывается статически, а это означает, что, поскольку статический тип mol1 равен Molecule, вызывается функция:
virtual double potential(const Molecule * mol) {return 4;}
и не
virtual double potential(const LC * mol) {return 5;}
Есть ли способ вызывать функцию динамически (в ООП-дизайне) без использования в ней typeid?
полный ответ:
После исследования с советом Питера, это оказалось классической проблемой двойной отправки, полное решение состоит в том, чтобы просто вызвать другой виртуальный объект внутри виртуального, как это:
#include <iostream>
#include <typeinfo>
#include <stdio.h>
using namespace std;
class Col;
class LC;
class Molecule
{
public:
/// more data and functions should be here regarding the molecule
double someBulshit;
virtual double potential(const Molecule * mol) const = 0;
virtual double potential(const Col * mol) const = 0;
virtual double potential(const LC * mol) const = 0;
};
class LC : public Molecule
{
public:
/// more data and functions should be here regarding the LC molecule
virtual double potential(const Molecule * mol) const {return mol->potential(this);}
virtual double potential(const LC * mol) const {return 2;}
virtual double potential(const Col * mol) const {return 3;}
};
class Col : public Molecule
{
public:
/// more data and function should be here regarding the Col molecule
virtual double potential(const Molecule * mol) const {return mol->potential(this);}
virtual double potential(const LC * mol) const {return 5;}
virtual double potential(const Col * mol) const {return 6;}
};
int main(int argc, char* argv[])
{
Molecule * mol1 = new Col();
Molecule * mol2 = new LC();
double my_potential = mol1->potential(mol2);
printf ("%f",my_potential);
}
и это действительно возвращает 3 по желанию.
1 ответ
Вы всегда вызываете потенциал (Молекула *), так как это тип аргумента, который вы передаете.
Если вы хотите сделать это динамически (т.е. принять решение во время выполнения), вам нужно будет выполнить динамическое приведение, чтобы решить, какой тип у вас есть, и затем вызвать нужную функцию. Например, вы можете реализовать функцию с аргументом базового типа только в базовом классе, проверить фактический тип и затем вызвать перегруженную функцию с правильным типом. Код может выглядеть так:
#include <iostream>
#include <typeinfo>
#include <stdio.h>
using namespace std;
class Col;
class LC;
class Molecule
{
public:
virtual ~Molecule() {}
/// more data and functions should be here regarding the molecule
double someBulshit;
double potential(const Molecule * mol);
virtual double potential(const Col * mol) = 0;
virtual double potential(const LC * mol) = 0;
};
class LC : public Molecule
{
public:
virtual ~LC() {}
/// more data and functions should be here regarding the LC molecule
virtual double potential(const LC * mol) {return 2;}
virtual double potential(const Col * mol) {return 3;}
};
class Col : public Molecule
{
public:
virtual ~Col() {}
/// more data and function should be here regarding the Col molecule
virtual double potential(const LC * mol) {return 5;}
virtual double potential(const Col * mol) {return 6;}
};
double Molecule::potential(const Molecule * mol) {
const Col *mol_as_Col = dynamic_cast<const Col*>(mol);
const LC *mol_as_LC = dynamic_cast<const LC*>(mol);
if(mol_as_Col) {
return potential(mol_as_Col);
}
else if(mol_as_LC) {
return potential(mol_as_LC);
}
else {
throw;
}
}
int main(int argc, char* argv[])
{
Molecule * mol1 = new Col();
Molecule * mol2 = new LC();
double my_potential = mol1->potential(mol2);
printf ("%f",my_potential);
}
Результатом будет 5, а не 6, как вы написали в своем вопросе, поскольку вы вводите тип LC и вызываете Col. Я также добавил недостающие виртуальные деструкторы, так как полиморфные классы всегда должны иметь его.
В качестве альтернативы, вы можете попробовать реализовать это с помощью кода шаблона и вообще избегать использования полиморфизма во время выполнения (т.е. никогда не держите указатели типа Molecule*). Если вам интересно, я могу привести пример, но, поскольку вы специально запрашиваете динамическое решение, это не похоже на ответ на этот вопрос.
РЕДАКТИРОВАТЬ: Здесь следует демонстрация шаблона. Это не имеет смысла без контекста. Я добавил функцию printPotential(), которая демонстрирует, как вы будете писать остальную часть кода:
#include <iostream>
#include <typeinfo>
#include <stdio.h>
using namespace std;
template<typename MOLECULE1, typename MOLECULE2>
void printPotential(MOLECULE1 *mol1, MOLECULE2 *mol2) {
double my_potential = mol1->potential(mol2);
printf("%f",my_potential);
}
class Col;
class LC
{
public:
/// more data and functions should be here regarding the LC molecule
double potential(const LC * mol) {return 2;}
double potential(const Col * mol) {return 3;}
};
class Col
{
public:
/// more data and function should be here regarding the Col molecule
double potential(const LC * mol) {return 5;}
double potential(const Col * mol) {return 6;}
};
int main(int argc, char* argv[])
{
Col *mol1 = new Col();
LC *mol2 = new LC();
printPotential(mol1, mol2);
}
На самом деле, когда я пишу это, я думаю, что может быть третий (и действительно предпочтительный) метод: вам нужно найти обобщенный алгоритм, как вычислять потенциал между двумя молекулами, используя только общую информацию, которая может быть частью базового класса (или получена). через виртуальные вызовы функций базового класса). В этом случае вы можете иметь одну единственную реализацию потенциал () (либо в качестве функции-члена базового класса с одним аргументом типа Molecule *, либо в качестве функции, не являющейся членом, с двумя аргументами типа Molecule*). Если я полагаю, что потенциал между двумя молекулами является разностью двух "абсолютных" потенциалов (просто как глупый пример), это может выглядеть так:
#include <iostream>
#include <typeinfo>
#include <stdio.h>
using namespace std;
class Col;
class LC;
class Molecule
{
public:
virtual ~Molecule() {}
/// more data and functions should be here regarding the molecule
double potential(const Molecule * mol) {
return mol->getAbsolutePotential() - getAbsolutePotential();
}
virtual double getAbsolutePotential() const = 0;
};
class LC : public Molecule
{
public:
virtual ~LC() {}
/// more data and functions should be here regarding the LC molecule
double getAbsolutePotential() const {return 42.0;}
};
class Col : public Molecule
{
public:
virtual ~Col() {}
/// more data and function should be here regarding the Col molecule
double getAbsolutePotential() const {return 120.0;}
};
int main(int argc, char* argv[])
{
Molecule * mol1 = new Col();
Molecule * mol2 = new LC();
double my_potential = mol1->potential(mol2);
printf ("%f",my_potential);
}