如何为一个虚拟函数提供多个重写

发布于 2024-10-08 03:35:33 字数 515 浏览 0 评论 0原文

我有以下课程:

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 是否可以实现这一目标? 如果没有动态转换是否可以实现这一目标?

谢谢

I have the following classes :

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"); }
};

Unfortunately, DoIt(B* b) will never get called.
DoIt(A* a) will always be called even if I add B objects to the list.

What can I do to make DoIt(B* b) called ?
Is it possible to achieve this if B does not know Q ?
Is it possible to achieve this if without dynamic cast ?

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

爺獨霸怡葒院 2024-10-15 03:35:33

好吧,没有人真正直接回答你的问题(好吧,重型尝试过)所以我会的。不过,这里的其他一些“答案”实际上对解决您的问题更有帮助。

问题是 void DoIt(B*) 不是虚函数 DoIt(A*) 的重写。这是一个超载。有很大的区别。

当你说当你传递 B* 时不会调用 DoIt(B*) 时,我必须假设你通过指向更高层次上的东西的指针持有指向你 Q 的引用或指针。在这些情况下,静态名称解析仅找到 DoIt(A*),并且由于 B* 是 A*,因此它会被向上转换,这就是被调用的版本。由于它是虚拟的,因此 Q 中的覆盖被称为。

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

当您有两个抽象类型和一个必须根据两个抽象的具体类型表现不同的函数时,您需要双重分派。当您在比静态命名提供的更高级别上使用 B 调用 Q 上的 DoIt 时,这就是您尝试执行的操作。有太多的方法可以满足不同的需求,无法针对您的情况提出一种解决方案而不是另一种解决方案,但并不真正知道您要解决什么问题。事实上,您可能根本不需要它!对您来说更好的方法可能是将 DoIt(B*) 实现为高层体系顶部的虚拟函数。

我建议您阅读 Andre Alexandrescu 的《Modern C++ Design》一书,并仔细阅读。他解释了一个非常酷的访问者实现以及可扩展的多重调度机制。但不要就此止步,还有其他出色的实现可以以不同的方式回答这个问题。

祝你好运。

Well, nobody's really directly answered your question (well, heavyd tried) so I will. Some other "answers" here are actually more helpful for fixing your problem though.

The issue is that void DoIt(B*) is NOT an override of the virtual function DoIt(A*). It's an overload. There's a HUGE difference.

When you say that DoIt(B*) is not called when you pass a B* I have to assume that you're holding references or pointers to you Q through a pointer to something higher up the higherarchy. In those cases the static name resolution only finds DoIt(A*) and since B* is-a A* it gets upcasted and that's the version that gets called. Since it is virtual the override in Q is what gets called.

If you had a pointer to Q as a pointer to Q though, and called DoIt with a B* the DoIt(B*) function should get called. At this point, double dispatch is not needed and is not used.

You need double dispatch when you have two abstract types and a function that must behave differently based on the concrete types of both abstractions. This is what you're attempting to do when you call DoIt with B on Q at a higher level than static naming provides. There are too many methods that answer different needs to be able to suggest one solution over another in your case, don't really know what you're trying to solve. In fact, you might not even need it! A better approach for you might be to implement DoIt(B*) as a virtual function in the top of your higherarchy.

I would suggest that you get Andre Alexandrescu's book, Modern C++ Design, and look it over. He explains a pretty darn cool visitor implementation as well as a multiple dispatch mechanism that scales. Don't stop there though, there's other great implementations that can answer the question differently.

Good luck.

梦里人 2024-10-15 03:35:33

您正在寻找一种未内置于该语言中的双重调度机制。根据访问者模式,可以采用不同的方法来实现这一点。 Google 在 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() 的参数的静态类型实际上是您要调用该函数的实际类型。

You are looking for a double dispatch mechanism that is not built into the language. There are different approaches on how this can be implemented based on the visitor pattern. Google for double-dispatch in C++. Note that this is a patch and not easily extended to big hierarchies:

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

The usual mechanism will be used and accept will be dispatched to the final overrider of the method, which in turn will call the visitor method. Now, at that point, the static type of the argument to the visitor operator() is in fact the actual type that you want to call the function with.

撩动你心 2024-10-15 03:35:33

DoIt(B* b) 永远不会被调用,因为您永远不会传入 B* 类型的对象,每次调用 DoIt 时,至少在给定的代码中,您正在传递 A* 类型的对象。

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

DoIt(B* b) will never get called because you are never passing in objects of type B*, every time you call DoIt, at least in the given code, you are passing in objects of type A*.

Consider the situation where the override of Doit(A* a) did not exist. Your current code would not compile because it the compiler cannot implicitly cast an object of type A* to B*.

如果有人传入 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" );
}

What are you expecting the behaviour to be if someone passes in an A* but the underlying type is really a B?

You might be looking for something like this:

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" );
}
寒尘 2024-10-15 03:35:33

您正在寻找多种调度或多方法。维基百科有一个关于 C++ 的很好的例子;链接此处

You're looking for multiple dispatch or multimethods. Wikipedia has a nice example for c++; link here.

書生途 2024-10-15 03:35:33

您尝试做的事情被称为 多重调度 并且在 C++ 中不起作用,因为函数重载是静态的。查看维基百科文章以了解一些可能的解决方法。

例如,如果您不希望 AB 类本身中的 DoIt 功能逻辑作为虚拟函数,那么您可以使用dynamic_cast方法:

class A {
};

class B : public A {
};

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

class Q : public P
{
protected:
 void DoIt(A* a) {
  if(B *b = dynamic_cast<B*>(a)) {
   // It's a B*, you can "use" b here
   print("true");
  } else {
   // It's an A*
   print("false");
  }
 }
};

What you are trying to do is known as multiple dispatch and won't work in C++ because function overloading is static. Take a look at the wikipedia article for some possible work arounds.

For example, if you don't want the logic for the DoIt functionality in the A and B classes themselves as a virtual function then you could use the dynamic_cast method:

class A {
};

class B : public A {
};

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

class Q : public P
{
protected:
 void DoIt(A* a) {
  if(B *b = dynamic_cast<B*>(a)) {
   // It's a B*, you can "use" b here
   print("true");
  } else {
   // It's an A*
   print("false");
  }
 }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文