c++ - 如何使用多态参数动态调用函数

标签 c++ oop polymorphism double-dispatch

当它们的静态类型都是父类时,如何动态调用形式为:childA.function(childB) 的函数?

以及更多细节:

我有一个物理项目,我需要计算 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 的情况下动态调用函数(在 OOP 设计中)?

完整答案:

After investigation with Peter advice this turn out to be classical double-dispatch problem the full solution is just to call another virtual inside the virtual like this:

#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。

最佳答案

您总是调用 potential(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 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);
}

实际上,在写这篇文章时,我认为可能有第三种(实际上是首选)方法:您需要找到一种抽象算法,如何通过仅使用可以作为基类一部分的公共(public)信息来计算两个分子之间的势能(或通过基类的虚函数调用获得)。在那种情况下,您可以有一个 potential() 的实现(作为基类的成员函数,带有一个 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);
}

关于c++ - 如何使用多态参数动态调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44883270/

相关文章:

java - 在 OO 中设计某些东西时

javascript - Google Data API 上的 OOP Javascript 回调方法

C++:在不违反 SRP 的情况下向多态类层次结构添加方法?

c++ - 实现一个进度条类

c++ - 当继承只为某些最终类型引入先决条件时如何记录/断言

c++ - 无论如何有一个多维 map ?

c++ - 是否在 C++ 头文件中声明类属性时遇到困难?

c++ - 通过构造函数制作的所有对象都具有相同的 vector

c++ - 同时处理基类的多个指针时如何处理多态性?

c# - 如何为一组具有几乎相同功能但具有不同参数和返回类型的类创建接口(interface)?