Как вызвать функцию динамически с аргументом полиморфизма

Как я могу динамически вызывать функцию вида: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 по желанию.

0

Решение

Вы всегда вызываете потенциал (Молекула *), так как это тип аргумента, который вы передаете.

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

#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);
}
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]