检测C++;虚函数已在派生类中重新定义 < > >强> >从一个C++基类指针指向一个派生类的实例,如何在运行时确定在派生类中是否重新实现了非纯虚函数(具有基类中的实现)?

检测C++;虚函数已在派生类中重新定义 < > >强> >从一个C++基类指针指向一个派生类的实例,如何在运行时确定在派生类中是否重新实现了非纯虚函数(具有基类中的实现)?,c++,virtual-functions,C++,Virtual Functions,上下文: 我正在编写一个C++库来解决某些数学等式。该库提供了一个包含多个虚拟函数的方程类,库用户将其用作他们希望求解的特定方程的基类。该库还提供了一个解算器类,该类将方程*作为构造函数参数。然后,用户按照以下行编写代码: class MyEquation : public Equation { ... } // equation definition here int main() { MyEquation myEqn; Solver solver(&myEqn); so

上下文: 我正在编写一个C++库来解决某些数学等式。该库提供了一个包含多个虚拟函数的
方程
类,库用户将其用作他们希望求解的特定方程的基类。该库还提供了一个
解算器
类,该类将
方程*
作为构造函数参数。然后,用户按照以下行编写代码:

class MyEquation : public Equation
{ ... } // equation definition here

int main()
{
  MyEquation myEqn;
  Solver solver(&myEqn);
  solver.Solve();
}
如果在导出的方程类中未重新定义
方程
中虚函数的某些组合,则可以省略
解算器
对象运行的算法中某些计算开销较大的部分。因此,我想知道,在
Solver
的构造函数中,哪些函数已被重新定义,哪些函数将运行
等式中的默认实现

  • 我想让它对库的用户透明,因此我不想寻找一种解决方案,例如,用户在其派生方程的构造函数中设置一些标志,指定哪些函数已被重新定义

  • 一种可能的解决方案是,
    等式
    中虚拟函数的默认实现在
    等式
    类中设置私有标志;然后,
    Solver
    类的构造函数可以清除此标志,运行虚拟函数,并检查标志值,以查看是否调用了
    等式
    中的实现。但我希望避免这种情况,因为每次执行虚拟函数时简单地设置标志会大大降低算法的速度(这些虚拟函数的执行对程序的运行时间有很大的影响,而默认实现只返回一个常量)


这不能用你正在尝试的方式来完成。基类的函数无法在没有明确告知的情况下知道它们实现的虚拟函数是否已被重写


看来你需要重新开始。不太确定我将如何解决您的设计问题,但我知道您现在尝试的方法根本行不通。

您无法通过便携方式检查虚拟函数的覆盖

您需要将知识带到
解算器
,最好通过类型而不是运行时标志

一种(基于类型的)方法是通过
dynamic\u cast
检查是否存在接口

一种可能更好的方法是提供solve函数的重载,在您的例子中是
Solver
构造函数


如果你能更具体地描述这个问题,可能会给出更好的建议。它确实让人想起了一个典型的情况:有人(1)需要解决一些问题P,(2)设想技术方法X作为P的解决方案,(3)发现X不能解决问题,(4)询问如何使X对P进行模糊描述,或者甚至对于一些不相关的问题Q。原始问题P的细节通常会提出比X更好的解决方案,以及使X工作的问题,与解决P无关。

即使这可能是某种原因,我建议不要这样做。你是:

  • 违反OOP原则。如果给了您一个通用类/接口,您不应该检查实现细节。这增加了类之间的依赖性,与OOP的设计目的正好相反
  • 复制代码。考虑到您使用的是等式类返回的常量
  • 混淆代码。类型检查的许多条件会使程序看起来很难看
  • 可能进行了过早的优化。执行虚拟函数调用和条件之间几乎没有速度差异。您是否运行了探查器来检查虚拟函数是否是瓶颈?在设计良好的应用程序/库中,这种情况几乎从未出现过

如果你没有使用虚拟方法呢

此时,我想到了一个基于模板的解决方案。使用模板来检测给定类是否有具有特定签名的方法是可能的(尽管不是那么容易)。一旦确定,您就可以在编译时在轻量级方案和重型方案之间切换

请注意,我并不建议对所有代码进行模板化,模板化的代码将只涵盖解决方案的
模板
(设计模式)部分,并且可以使用常规函数来减少依赖性


此外,对于客户端,这可以是完全透明的,只要您不更改方法的签名。

< P>我不知道如何进行这样的检测,但是您是否考虑使用静态多态性?如果将等式的每个虚拟方法替换为具有默认值的模板“policy”,则可以在编译时进行优化

//default implementation of a virtual method turned into a functor
//which optionally takes a reference to equation
class DefaultFunctor1
{
public:
    //...
    double operator()(double x) { return log(x); }
};
template<class F1 = DefaultFunctor1>
class Equation
{
public:
    typedef F1 Functor1;
    //...
private:
    F1 f1;
};
class Solver
{
public:
    //....
    template<class Equation>
    void solve(Equation& eq)
    {
        Loki::Int2Type<
                        Loki::IsSameType<Equation::Functor1, DefaultFunctor1>::value
                       > selector;
        //choose at compile time appropriate implementation among overloads of doSolve
        doSolve(selector);
    }
private:
    //.... overloads of doSolve here
};
int main()
{
    Equation<> eq;
    Solver solver;
    solver.solve(eq); //calls optimized version
    Equation<MyFunctor> my_eq;
    solver.solve(my_eq); //calls generic version
    return 0;
}
//虚拟方法转化为函子的默认实现
//它可以选择引用公式
类DefaultFunctor1
{
公众:
//...
双运算符()(双x){返回日志(x);}
};
模板
类方程
{
公众:
typedef F1 functor 1;
//...
私人:
F1;
};
类求解器
{
公众:
//....
模板
空洞求解(方程和等式)
{
Loki::Int2Type<
Loki::IsSameType::value
>选择器;
//在编译时从doSolve的重载中选择适当的实现
多索尔夫(selec)
#include <iostream>

struct X {
    virtual void x () = 0;
};

struct Y {
    virtual void y () = 0;
};


struct Foo {
    virtual ~ Foo () {}

    bool does_x () {
        return NULL != dynamic_cast <X*> (this);
    }

    bool does_y () {
        return NULL != dynamic_cast <Y*> (this);
    }

    void do_x () {
        dynamic_cast <X&> (*this) .x ();
    }

    void do_y () {
        dynamic_cast <Y&> (*this) .y ();
    }
};

struct foo_x : public Foo, public X {
    void x () {
        std :: cout << __PRETTY_FUNCTION__ << std :: endl;
    }
};


struct foo_y : public Foo, public Y {
    void y () {
        std :: cout << __PRETTY_FUNCTION__ << std :: endl;
    }
};

struct foo_xy : public Foo, public X, public Y {
    void x () {
        std :: cout << __PRETTY_FUNCTION__ << std :: endl;
    }

    void y () {
        std :: cout << __PRETTY_FUNCTION__ << std :: endl;
    }
};

void test (Foo & f)
{
    std :: cout << &f << " "
        << "{"
        << "X:" << f .does_x () << ", "
        << "Y:" << f .does_y ()
        << "}\n";

    if (f .does_x ())
        f .do_x ();

    if (f .does_y ())
        f .do_y ();
}

int main ()
{
    foo_x x;
    foo_y y;
    foo_xy xy;
    test (x);
    test (y);
    test (xy);
}
   class Base { virtual int foo(); }
   class Derived : public Base { virtual int foo(); }

   bool Xxx::checkOverride()
   {
       int (Base::*fpA)();
       int (Base::*fpb)();

       fpA = &Base::foo;
       fpB = &Derived::foo;

       return (fpA != fpB);
   }
(void*)(obj.*(&Interface::method)) != (void*)(&Interface::method)
#include <iostream>
#include <typeinfo>

struct A { virtual void Foo() {} };
struct B : public A { void Foo() {} };
struct C : public A { };

int main() {
    std::cout << int(typeid(&A::Foo) == typeid(&A::Foo)) << std::endl;
    std::cout << int(typeid(&A::Foo) == typeid(&B::Foo)) << std::endl;
    std::cout << int(typeid(&A::Foo) == typeid(&C::Foo)) << std::endl;
    return 0;
}