Reputation: 71
I have a base class and n derived class. I want to instantiate a derived class and send it to a function that receive as an argument a base class. Inside the function, I found which type of derived class it is by using dynamic_cast, but I don't want to use several if-else sentences. Instead, I would like to know if there is a way to find out which derived class is it in order to cast it. Here I leave my code as an example.
class animal{
public:
virtual ~animal() {}
int eyes;
};
class dog: public animal{
public:
int legs;
int tail;
};
class fish: public animal{
public:
int mostage;
};
void functionTest(animal* a){
if(dynamic_cast<fish*>(a) != NULL){
do_something();
}
else if(dynamic_cast<dog*>(a) != NULL){
do_something();
}
};
I would like to have a more general approach to this. Something like dynamic_cast(a). Thank you!
Upvotes: 2
Views: 1130
Reputation: 829
The "classical" type-switch by Stroustrup may be suitable for your needs: https://parasol.tamu.edu/mach7/ https://parasol.tamu.edu/~yuriys/pm/
Basically it will let you do a switch-case like based on obect type, using one of three different implementations
Upvotes: 1
Reputation: 5728
It's great to do this for quick drafts if you need to demonstrate something in a few minutes, but usually you try to avoid using dynamic_cast this way - it can lead to extremely high maintenance costs if used in the wrong places. Various patterns are available, such as a simple method overload, the Visitor pattern, or a virtual "GetType" function (which could be implemented with the curiously recurring template pattern, if you like patterns).
I'll list all 3 approaches. The first one is by far the most straightforward, and easiest to use. The advantages of the other 2 is that each of them moves the decision of what to do to a different part of the code, which can be a huge benefit (or drawback).
Lets assume this is what you want to do:
void functionTest(animal* a)
{
if(dynamic_cast<fish*>(a) != NULL)
blub();
else if(dynamic_cast<dog*>(a) != NULL)
bark();
};
Simple virtual function approach:
class animal {
public:
virtual ~animal() {}
virtual void do_something() = 0;
int eyes;
};
class dog : public animal {
public:
virtual void do_something() { bark(); } // use override in C++11
int legs;
int tail;
};
class fish: public animal {
public:
virtual void do_something() { blub(); } // use override in C++11
int mostage;
};
void functionTest(animal* a)
{
if (a) a->do_something();
};
Visitor approach:
class IVisitor {
public:
~IVisitor(){}
virtual void visit(const fish&){}
virtual void visit(const dog&){}
virtual void visit(const animal&){}
};
class animal {
public:
virtual ~animal() {}
virtual void accept(IVisitor& visitor) = 0;
int eyes;
};
class dog : public animal {
public:
virtual void accept(IVisitor& visitor) { visitor.visit(*this); } // use override in C++11
int legs;
int tail;
};
class fish : public animal {
public:
virtual void accept(IVisitor& visitor) { visitor.visit(*this); } // use override in C++11
int mostage;
};
class MyVisitor : public IVisitor {
public:
virtual void visit(const fish&) { blub(); } // use override in C++11
virtual void visit(const dog&) { bark(); } // use override in C++11
};
void functionTest(animal* a)
{
if (a)
{
MyVisitor v;
a->accept(v);
}
};
GetType approach, with CRTP spice:
class animal {
public:
virtual ~animal() {}
virtual const type_info& getType() const = 0; // careful. typeinfo is tricky of shared libs or dlls are involved
int eyes;
};
template <class T>
class BaseAnimal : public animal {
// these are C++11 features. Alternatives exist to ensure T derives from BaseAnimal.
static_assert(std::is_base_of<BaseAnimal,T>(,"Class not deriving from BaseAnimal");// C++11
virtual const type_info& getType() const { return typeid(T); }
};
class dog : public BaseAnimal<dog> {
public:
int legs;
int tail;
};
class fish : public BaseAnimal<fish> {
public:
int mostage;
};
void functionTest(animal* a)
{
if (!a)
return;
if (a->getType() == typeid(fish))
blub();
else if (a->getType() == typeid(dog))
bark();
};
Notice that you should consider the above examples to be pseudo-code. For best practices you will need to look up the patterns. Also, the curiously recurring template pattern can also be used in the second approach, or it can be easily removed from the third. It's just for convenience in these cases.
Upvotes: 7
Reputation: 8926
Let me strongly urge you to NOT do what you have here and to follow the very wise advice that everyone has given to use polymorphism (e.g. virtual functions). The approach you've outlined could be made to work, but it is working against the tools the language provides. What you are trying to do is exactly why the language has virtual functions.
With your method, if you add a new sub-class of animal
then you also have to change function_test()
, and function_test()
is doing what the compiler would do for virtual functions anyway, but in a much clumsier and inefficient way.
Using virtual functions, all you have to do is implement do_something()
in the new sub-class and the compiler takes care of the rest.
Don't use dynamic_cast<>()
for this. That's not what it is for.
Upvotes: 3
Reputation: 217438
You may use virtual functions for that:
class animal{
public:
virtual ~animal() {}
virtual void do_thing() = 0;
};
class dog: public animal{
public:
void do_thing() override { std::cout << "I'm a dog" << std::endl; }
};
class fish: public animal{
public:
void do_thing() override { std::cout << "I'm a fish" << std::endl; }
};
And then
void functionTest(animal& a){
a.do_thing();
}
As an alternative, if you want to avoid to have to many virtual functions, you may use visitor pattern
Upvotes: 3
Reputation: 43979
Consider implementing this "switch" as virtual functions.
If you do not want that, you can either use dynamic_cast
as in your example, or you use the typeid operator to compute a mapping for the result of typeid
to a function that implements the do_something
code.
However, I would not recommend that, as you just end up with a hand-coded vtable. It is better to use virtual functions and let the compiler generate the mapping.
For additional reading, I recommend Herb Sutter's article Type inference vs static/dynamic typing. He mentions Boost variant and Boost any, which might be possible alternatives for your problem.
Upvotes: 2