How to create polymorphic object


Keywords:c++ 


Question: 

I have a class, which unites some structures, for example like this:

struct _tRack1{
    unsigned char shelf1;
    unsigned int shelf2;
    float shelf3;
};

struct _tRack2{
    char shelf1;
    int shelf2;
    char shelf3;
    char shelf4;
};

struct _tRack3{
    char shelf1;
    unsigned int drawer[5];
};
class Catalog
{
public:
    _tRack1 *localdata1;
    _tRack2 *localdata2;
    _tRack3 *localdata3;
    int index;
    Catalog(int recktype){
        localdata1 = NULL;
        localdata2 = NULL;
        localdata3 = NULL;
        index = recktype;
        switch(recktype){
            case 1: *localdata1 = new _tRack1; break;
            case 2: *localdata2 = new _tRack2; break;
            case 3: *localdata3 = new _tRack3; break;
        }
    };
    ~Catalog(){
        if(localdata1 != NULL) delete localdata1;
        if(localdata2 != NULL) delete localdata2;
        if(localdata3 != NULL) delete localdata3;
    };
    int someMethod(_tRack1){/*...*/};
    int someMethod(_tRack2){/*...*/};
    int someMethod(_tRack3){/*...*/};
};

int main()
{
    Catalog *foo = new Catalog(1);
    Catalog *bar = new Catalog(3);

    /*...*/
    if(foo->index>1) foo->localdata1->shelf1=-3;
       else foo->localdata1->shelf1=3;
    if(bar->index>1) bar->localdata1->shelf1=-3;
       else bar->localdata1->shelf1=3;
    if(bar->index==3) bar->localdata3->drawer[0] = 0xDEADBEAF;


    /*...*/
    delete foo;
    delete bar;
    return 0;
}

I know it is not good to make structures public, but in real life structures are very complicated so it is impossible to create methods for access to separate fields of structures. I want to find a way to hide a structure type. To get access to data like this:

    if(foo->index>1) foo->data->shelf1=-3;
       else foo->data->shelf1=3;
    if(bar->index>1) bar->data->shelf1=-3;
       else bar->data->shelf1=3;
    if(bar->index==3) bar->data->drawer[0] = 0xDEADBEAF;

Is it possible?


3 Answers: 

You should throw away anything that you have done so far. Then read a book about object oriented programming and than start again from scratch.

In simplicity, you should create a base class Rack and then inherit specific Rack types (i.e. derived classes) from it. The Catalogue nor any other class should not know the details of objects derived from Rack. It should ask the Rack-derived objects to do actions by calling their virtual methods rather than ask for/access their private details.

Moreover - forget about the existence of new and delete now. You do not need it yet. It makes your code worse.



It should be created a get method in Catalog which can return all members from structs after a specified parameter. So as:

void Catalog::getValue(char* member, char &value)
{
switch(index)
{
  case 2:
    if(member="shelf1") value = localdata2->shelf1;
    if(member="shelf3") value = localdata2->shelf3;
  break;
  case 3:
    if(member="shelf1") value = localdata3->shelf1;
  break;
  case 3: break;   
}   
}

and an other to float values and so on.



As you access your catalog (items) only via pointers, it's a good candidate for an abstract class. The racks would then inherit from this base class:

class CatalogItem {   // I've just rename it not to confus with yours
private: 
    int index;          // you need this 
public: 
    CatalogItem(int recktype) : index(recktype) { }   
    int getType() { return index; }
    virtual ~CatalogItem() {}       // Virtal destructor so that the real class destructor is called
    virtual int someMethod() = 0;   // Pure virtual function.  You can never create object directly 
};

THen you can create your struct or class _tRack1 as follows:

class _tRack1 : public CatalogItem {
public: 
    unsigned char shelf1;    // Original structure, but better'd hide it later
    unsigned int shelf2;
    float shelf3;

    _tRack1() : CatalogItem (1) {}; 
    int someMethod() { /* implementation for type 1 */ return 0; };
};

Do similar for _tRack2 and _tRack3. Your main would look like:

CatalogItem *foo = new _tRack1;  // pointer to a real consistent object
CatalogItem *bar = new _tRack3;
...
if (foo->getType()==1)  static_cast<_tRack1*>(foo)->shelf1 = 3; // object specific setting
foo->someMethod();   // call of method.  You don't have to care about index anymore.