Search code examples
c++inheritancedynamic-casttypechecking

Proper way to identify type of derived class(type entity VS dynamic_case)


I'm trying to implement data classes derived from one base class.

And each derived classes has different data fields.

So the each instances have to be handled differently depending on type of data instance.

I made one example code for this.

#include <iostream>
#include <algorithm>
#include <boost/shared_ptr.hpp>
using namespace std;

enum DataTypes{
    EMPTY = 0,
    TYPE1,
    TYPE2
};
class DataBase {
public:
    DataBase():type(EMPTY){}
    virtual ~DataBase(){}
    DataTypes getType() const{return type;}

protected:
    DataBase(DataTypes n):type(n){}
    DataTypes type;
};

class DataType1 :public DataBase {
public:
    DataType1(): DataBase(TYPE1){}

    string data_for_class1;
};

class DataType2 :public DataBase {
public:
    DataType2(): DataBase(TYPE2){}

    string data_for_class2;
};

boost::shared_ptr<DataBase> createInstance(int n){
    boost::shared_ptr<DataBase> p;
    if(n == 1){
        p.reset(new DataType1);
        boost::shared_ptr<DataType1> temp = boost::static_pointer_cast<DataType1>(p);
        temp->data_for_class1 = "[Data for DataType1]";
    }
    else if(n==2){
        p.reset(new DataType2);
        boost::shared_ptr<DataType2> temp = boost::static_pointer_cast<DataType2>(p);
        temp->data_for_class2 = "[Data for DataType2]";
    }

    return p;
}

int main() {
    boost::shared_ptr<DataBase> p = createInstance(2);
    try{
        /*
        if p is an instance of DataType1
            process p as DataType1;
        else if p is an instance of DataType2
            process p as DataType2;
        else
            throw exception("Empty data");
        */
    }catch(runtime_error& e){
        cerr<<e.what()<<endl;
    }

    return 0;
}

The commented part in try-catch expression should be filled.

After thinking hard,I got two possible solutions.

1. checking type field in DataBase class

For doing this,

  • DataBase class should have type field.
  • Every classes derived from DataBase are responsible for calling constructor(DataBase(DataTypes n)) with appropriate parameter.

Code :

switch (p->getType()){
    case TYPE1:
    {
        cout<<"This is an instance of DataType1"<<endl;
        boost::shared_ptr<DataType1> temp = boost::static_pointer_cast<DataType1>(p);
        cout<<temp->data_for_class1<<endl;
    }
        break;
    case TYPE2:
    {
        cout<<"This is an instance of DataType2"<<endl;
        boost::shared_ptr<DataType2> temp = boost::static_pointer_cast<DataType2>(p);
        cout<<temp->data_for_class2<<endl;
    }
        break;
    case EMPTY: default:
        throw runtime_error("Data is empty");
}

2. using dynamic_cast for all derived classes

In contrast to case1, this method

  • does not require DataBase to have additional field for type-checking.
  • But I don't know dynamic_cast is good way of doing this.

Code :

if(boost::dynamic_pointer_cast<DataType1>(p)){
    cout<<"This is an instance of DataType1"<<endl;
    boost::shared_ptr<DataType1> temp = boost::static_pointer_cast<DataType1>(p);
    cout<<temp->data_for_class1<<endl;
}
else if(boost::dynamic_pointer_cast<DataType2>(p)){
    cout<<"This is an instance of DataType2"<<endl;
    boost::shared_ptr<DataType2> temp = boost::static_pointer_cast<DataType2>(p);
    cout<<temp->data_for_class2<<endl;
}
else{
    throw runtime_error("Data is empty");
}

Both of the above ways, I could get working codes. But I'm not sure this is proper way of doing this.

If the above codes have potential problems or if you know a better solution, please share your nice idea.

Thanks.


Solution

  • There is also solution #3 with a virtual getType (and no type field stored in the base).

    class DataBase {
    public:
        DataBase() {}
        virtual ~DataBase() {}
    
        virtual DataTypes getType() { return EMPTY; } // or '= 0;'
    };
    
    class DataType1 :public DataBase {
    public:
        //...
        DataTypes getType() { return TYPE1; };
    };
    
    class DataType2 :public DataBase {
    public:
        //...
        DataTypes getType() { return TYPE2; };
    };