Prokop Hapala
Prokop Hapala

Reputation: 2444

Store pointers of different type in one array

I want to store pointers to objects of different classes in one data-structure ( such as array, HashMap ... ). According to this answer I came out to following solution using untyped pointer void* and operator &*( ClassName* ) for pointer type recasting:

#include <stdio.h>
class ClassA{ public: int    a;  };
class ClassB{ public: double b;  };
class Storage{
    public:
    int n;
    void** store;
    Storage( int n_ ){ n = n_; store = new void*[n]; };
};
Storage s( 5 );
int main(){
    // test store and load ClassA
    ClassA a1; a1.a = 16;           
    s.store[ 0 ] = &a1;
    ClassA* a2 = &*( ClassA* ) s.store[ 0 ];
    printf(  " a2 %i \n", a2->a );
    // test store and load ClassB
    ClassB b1; b1.b = 15.1455;
    s.store[ 1 ] = &b1; 
    ClassB* b2 = &*( ClassB* ) s.store[ 1 ];
    printf(  " b2 %f \n", b2->b );
}

Now questions:

  1. Is anything bad about typecasting like &*( ClassB* ) except that it is not type safe?
  2. Is there any performance cost for this typecasting ?
  3. Is there any better ( nicer, more safe, faster ) way how store pointer to different objects of different class in a single array?

Ah, I figured out that it is actually not usefull to make it void** because than I have now way how to recognize type of the stored object later. Better would be do it with template, and common super class. Like this

#include <stdio.h>
#include <cstdlib>
template< class TYPE1 > class Storage{
    public:
    int n;
    TYPE1** store;
    Storage( int n_ ){ n = n_; store = new TYPE1*[n];     };  // Constructor
    void   put( int key, TYPE1* obj ){ store[key] = obj;  };  // save data
    TYPE1* get( int key             ){ return store[key]; };  // load data
};
class Parent { public: int    kind;                                                                     };
class ChildA : public Parent { public: int    someData;   ChildA( int    someData_  ){ kind=0; someData =someData_;  }  };
class ChildB : public Parent { public: double otherData;  ChildB( double otherData_ ){ kind=1; otherData=otherData_; }  };
Storage<Parent> mixed( 10 );
int main(){
    srand( 15454 );
    printf( " ==== initialize by random data and radom type \n" ); 
    for(int i=0; i<mixed.n; i++ ){ 
        if   ( (rand()&4) > 0 ){ 
            int someData = rand()&0xFF; 
            mixed.put( i, new ChildA(  someData ) );
            printf( " i %i ChildA.someData  = %i \n", i , someData );    
        }else{  
            double otherData =  (rand()&0xFF)/256.0f; 
            mixed.put( i, new ChildB(  otherData ) );
            printf( " i %i ChildB.otherData = %f \n", i , otherData );  
        };
    };
    printf( " ==== recall stored data \n" ); 
    for(int i=0; i<mixed.n; i++ ){ 
        Parent* obj = mixed.get( i );
        if   ( obj->kind ==0 ){ printf( " i %i ChildA.someData  = %i \n", i , ( (ChildA *) obj )->someData  ); }
        else                  { printf( " i %i ChildB.otherData = %f \n", i , ( (ChildB *) obj )->otherData ); };
    };
}

Upvotes: 0

Views: 1574

Answers (1)

Brian Bi
Brian Bi

Reputation: 119164

Is anything bad about typecasting like &*( ClassB* ) except that it is not type safe?

Applying * followed by & to a pointer is a no-op, so simply (ClassB*) should suffice. Also you should be using C++-style casts, in this case static_cast<ClassB*>, instead of C-style casts, to make your meaning more explicit.

The bad thing is that not only is this not type-safe, but it's also very confusing and an abuse of the C++ language.

Is there any performance cost for this typecasting ?

No.

Is there any better ( nicer, more safe, faster ) way how store pointer to different objects of different class in a single array?

Yes, the best way is to factor out common behaviour of those types into a base class (possibly abstract), and declare your array to store pointers to that base class. If there is no common behaviour, then storing them all in the same array is almost always the wrong thing to do. If for some reason you really want to do this, something like Boost.Any might be the right solution.

Upvotes: 3

Related Questions