How to provide more than one overrides for one virtual function
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:开发者_StackOverflow
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
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.
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.
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*
.
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" );
}
You're looking for multiple dispatch or multimethods. Wikipedia has a nice example for c++; link here.
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");
}
}
};
精彩评论