如何为一个虚函数提供多个覆盖

时间:2010-12-16 17:08:46

标签: c++ polymorphism override

我有以下课程:

class A {
};

class B : public A {
};

class P     {
private:
 std::list<A*> l
protected:
 virtual void DoIt(A* a) = 0;
public:
 void WorkerThread() { for (it=l.begin(); it!=l.end(); it++) DoIt(*it); }
};

class Q : public P
{
protected:
 void DoIt(A* a) { print("false"); }
 void DoIt(B* b) { print("true"); }
};

不幸的是,DoIt(B * b)永远不会被召唤。 即使我将B对象添加到列表中,也总会调用DoIt(A * a)。

如何调用DoIt(B * b)? 如果B不知道Q,是否有可能实现这一目标? 如果没有动态演员,是否有可能实现这一目标?

谢谢

6 个答案:

答案 0 :(得分:4)

嗯,没有人真的直接回答了你的问题(好吧,我试过),所以我愿意。这里的其他一些“答案”实际上对解决问题更有帮助。

问题是void DoIt(B *)不是虚函数DoIt(A *)的重写。这是一个重载。有一个巨大的差异。

当你说在传递B *时没有调用DoIt(B *)时,我必须假设你通过指向更高级别的东西来指向你的引用或指针。在这些情况下,静态名称解析只能找到DoIt(A *),因为B *是-A A *它会被上升,而且它是被调用的版本。由于它是虚拟的,Q中的覆盖是被调用的。

如果有一个指向Q的指针作为指向Q的指针,并且用B *调用DoIt,则应该调用DoIt(B *)函数。此时,不需要双重调度,也不使用。

当你有两个抽象类型和一个必须根据两个抽象的具体类型表现不同的函数时,你需要双重调度。这是你在Q上以高于静态命名提供的方式调用DoIt时尝试做的事情。有太多的方法可以满足不同的需求,能够在您的情况下建议一个解决方案,而不是真正知道您要解决的问题。事实上,你可能甚至不需要它!更好的方法可能是将DoIt(B *)作为虚拟函数实现在高层次的顶层。

我建议您阅读Andre Alexandrescu的书“现代C ++设计”,并仔细研究。他解释了一个非常酷的访问者实现以及可扩展的多重调度机制。不要停在那里,还有其他很棒的实现可以用不同的方式回答这个问题。

祝你好运。

答案 1 :(得分:2)

您正在寻找一种未内置于该语言中的双重调度机制。关于如何根据访客模式实现这一点有不同的方法。谷歌在C ++中进行双重调度。请注意,这是一个补丁,不容易扩展到大型层次结构:

struct visitor;
struct A {
   virtual void accept( visitor& v ) { v(*this); }
};
struct B {
   virtual void accept( visitor& v ) { v(*this); }
}; 
struct visitor {
   virtual void operator()( A& ) = 0;
   virtual void operator()( B& ) = 0; 
};
struct myvisitor : visitor {
   void operator( A& ) { std::cout << "A" << std::endl; }
   void operator( B& ) { std::cout << "B" << std::endl; }
};
int main() {
   std::vector<A*> data = ...
   myvisitor v;
   for ( std::vector<A*>::iterator it = data.begin(), end = data.end(); it != end; ++it )
   {
      (*it)->accept( v );
   }
}

将使用通常的机制,accept将被分派到方法的最终覆盖者,而后者将调用访问者方法。现在,此时,访问者operator() 的参数的静态类型实际上是要用函数调用函数的实际类型。

答案 2 :(得分:1)

DoIt(B* b)将永远不会被调用,因为你永远不会传入B *类型的对象,每次调用DoIt时,至少在给定的代码中,你传入的类型为{{{ 1}}。

考虑不存在A*覆盖的情况。您当前的代码无法编译,因为编译器无法将Doit(A* a)类型的对象隐式转换为A*

答案 3 :(得分:0)

如果有人传入A *但基础类型真的是B,你对这种行为的期望是什么?

你可能正在寻找这样的东西:

class A
{
public:
  virtual ~A() {}
  virtual bool isB() const { return false; }
};

class B : public A
{
public:
  bool isB() const { return true; }
};

void Q::DoIt( A* a )
{
   print( a->isB() ? "true" : "false" );
}

答案 4 :(得分:0)

您尝试执行的操作称为multiple dispatch,但在C ++中无效,因为函数重载是静态的。请查看维基百科文章,了解可能的解决方法。

例如,如果您不希望DoItA类中的B功能的逻辑作为虚函数,那么您可以使用{{1}方法:

dynamic_cast

答案 5 :(得分:0)

您正在寻找多种调度或多种方法。维基百科有一个很好的c ++示例;链接here