检测是否已在派生类中重新定义C++虚函数的方法

Chr*_*son 33 c++ virtual-functions

简而言之:从指向派生类实例的C++基类指针,如何在运行时确定是否已重新实现非纯虚函数(在基类中具有实现)派生类?

上下文: 我正在编写一个C++库来解决某些类别的数学方程.该库提供了一个Equation具有多个虚函数的类,库用户将其用作他们希望解决的特定等式的基类.该库还提供了一个Solver类,它接受Equation *一个构造函数参数.然后用户按以下方式编写代码:

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

int main()
{
  MyEquation myEqn;
  Solver solver(&myEqn);
  solver.Solve();
}
Run Code Online (Sandbox Code Playgroud)

如果虚拟函数的某些组合Equation未在导出的方程类中重新定义,Solver则可以省略由对象运行的算法的某些计算上昂贵的部分.因此,我想知道,在构造函数中Solver,哪些函数已被重新定义,而是将运行默认实现Equation.

  • 我想让这对图书馆的用户透明,所以我不是在寻找一个解决方案,例如,用户在派生方程的构造函数中设置一些标志,指定哪些函数已被重新定义.

  • 一种可能的解决方案是在虚拟函数的默认实现中Equation设置类中的私有标志Equation; Solver然后,类的构造函数可以清除此标志,运行虚函数,并检查标志值以查看是否Equation已调用实现.我想避免这种情况,因为每次执行虚函数时只需设置标志就会使算法减慢很多(这些虚函数的执行对程序的运行时间有很大帮助,而默认实现只返回一个常数).

Che*_*Alf 19

您无法移植地检查虚拟功能的覆盖.

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

一种(基于类型)方式是通过检查接口的存在或不存在dynamic_cast.

一种可能更好的方法是提供solve函数的重载,在你的情况下是Solver构造函数.

如果您提供更具体的问题描述,可能会给出更好的建议.它确实提醒了某人(1)需要解决某些问题P的典型情况,(2)设想技术方法X作为P的解决方案,(3)发现X不削减它,(4)询问如何使X适用于P的模糊描述,甚至是一些不相关的问题Q.原始问题P的细节通常会提出比X更好的解决方案,以及使X工作的问题,与解决P.无关.

  • 最后一段是我的一天.+1 (7认同)
  • 谢谢.我总体上同意你的最后一段 - 在我看来,虽然在某些情况下X的答案(说"不,因为......")对SO来说是一个更有用的东西(可能对问题更有用) -asker,长期)而不是对P的回答. (2认同)

Joh*_*ith 12

为了将来参考,结果是GCC提供了这个扩展:http://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html,它允许检查方法是否被覆盖

(void*)(obj.*(&Interface::method)) != (void*)(&Interface::method)
Run Code Online (Sandbox Code Playgroud)

ICC正式支持此扩展,clang的文档没有提及它,但代码工作,甚至编译没有警告.

但是,MSVC并不支持这一点,所以就是这样.

此外,如果您链​​接到实现已更改的库的不同版本,它似乎无法使用单独库中的标头(即内联)中定义的方法.如果我正确地解释了标准,这是未定义的行为(改变实现),但如果实现保持不变,那么地址也应该是唯一的.所以不要使用内联方法.


jK.*_*jK. 7

在找到链接到gcc的PMF扩展名的结果后,我认为必须有一个正确的方法.

我找到了一个没有任何黑客攻击的解决方案,至少经过测试可以在gcc&llvm上工作:

#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;
}
Run Code Online (Sandbox Code Playgroud)

http://ideone.com/xcQOT6

PS:我实际上是在CEventClient系统中使用它.因此,您从CEventClient派生您的类,如果它覆盖了一个事件方法,它将自动"链接"该事件.

  • 您也可以使用`std :: is_same <decltype(&A :: Foo),decltype(&B :: Foo)> :: value`,而不是比较`typeid`s.不幸的是,它仍然是一个编译时检查,而不是OP要求的运行时检查. (3认同)
  • 投反对票,因为这根本不能回答问题。问题是如何检测 Foo() 是否已被覆盖,仅给出一个指向基类 A 的指针(并且不知道类 B 和 C )。 (3认同)