问题
This may be a stupid question, I suspect I know the answer (no) because I seem to be hitting a wall here.
Given I have a collection of objects derived from certain class:
class BaseClass;
class DerivedA: public BaseClass;
class DerivedB: public BaseClass;
class DerivedC: public BaseClass;
std::vector<BaseClass> myCollection;
I want to call a method depending on the types of the specific class:
class Processor {
void doSomething(DerivedA a, DerivedB b);
void doSomething(DerivedA a, DerivedC c);
}
The problem is, if I access the individual items on the collection and try to call the 'doSomething' method in the 'Processor', it will not be able do decide which method to use (afaik). So my question is: Is there any way to fetch the items in the collection with the right derived-type?
回答1:
If you are going to keep the doSomething
method as it is, this is what is called multiple dispatch and is NOT currently supported by C++.
If it were a virtual member function of BaseClass
then yes it would be the run of the mill C++ polymorphism on the object it is being invoked on, but it would still NOT automatically infer the type of the arguement.
To get around this you can do something like what is suggested in the earlier link
void collideWith(Thing& other) {
// dynamic_cast to a pointer type returns NULL if the cast fails
// (dynamic_cast to a reference type would throw an exception on failure)
if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
// handle Asteroid-Asteroid collision
} else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
// handle Asteroid-Spaceship collision
} else {
// default collision handling here
}
}
Basically keep casting to various possible Derived classes until one works and call one of the methods appropriately(no special effort since the compiler knows what type you are trying to cast to).
IMPORTANT: as @WhozCraig points out, your vector needs to hold pointers to avoid Object-Slicing and render this whole question moot.
回答2:
Ok, yes you should use polymorphism as the above stated. If your function needs to handle 2 objects though it gets extremely complicated.
If the derivations form a limited set and know each other you can use double-dispatch. It's not perfect but it solves this particular case.
class DerivedA;
class DerivedB;
class DerivedC;
class BaseClass
{
public:
virtual ~BaseClass();
virtual void doSomethingWithBase( BaseClass & b2 ) = 0;
virtual void doSomethingWithDerivedA( DerivedA & da ) = 0;
virtual void doSomethingWithDerivedB( DerivedB & db ) = 0;
virtual void doSomethingWithDerivedC( DerivedC & dc ) = 0;
};
class DerivedA : public BaseClass
{
public:
void doSomethingWithBase( BaseClass & b2 )
{
b2.doSomethingWithDerivedA( *this );
}
void doSomethingWithDerivedA( DerivedA & da )
{
// implement for two DerivedA objects
}
void doSomethingWithDerivedB( DerivedB & db )
{
// implement for an A and B
}
void doSomethingWithDerivedC( DerivedC & dc )
{
// implement for an A and C
}
};
// implement DerivedB to call doSomethingWithDerivedB on its parameter
// implement DerivedC to call doSomethingWithDerivedC on its parameter.
You get the idea. From where you call you don't need to know which two types you have and you never need to actually look this up. But if you ever add more implementations you have a lot of code to edit and may consider some kind of lookup table.
If you need a class to define itself you can use some kind of virtual id.
class BaseClass
{
public:
virtual int id() const = 0;
};
and then you get the classes to reveal their ids and find the handler in the table based on these ids that wil handle the two objects. The ids don't have to be ints, they can be strings which makes it easier to avoid naming clashes, and this has the advantage over the double-dispatch method of the base class not knowing its derived classes or them knowing each other, and being extensible. You also don't have to handle every pair.
来源:https://stackoverflow.com/questions/14056609/is-there-a-way-to-infer-the-type-of-an-object