Хранить указатели разного типа в одном массиве

0

Я хочу хранить указатели на объекты разных классов в одной структуре данных (например, массив, HashMap...). В соответствии с этим ответом я пришел к следующему решению с использованием нетипизированного указателя void* и оператора &*( ClassName* ) для &*( ClassName* ) типа указателя:

#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 );
}

Теперь вопросы:

  1. Что-то плохое в отношении типов, подобных &*( ClassB* ) за исключением того, что оно не безопасно для типа?
  2. Есть ли какая-либо стоимость для этой типизации?
  3. Есть ли лучший (более удобный, более безопасный, быстрый) способ хранения указателя на разные объекты другого класса в одном массиве?

Ах, я понял, что на самом деле не полезно делать это void** потому что теперь у меня есть способ узнать тип хранимого объекта позже. Лучше было бы делать это с помощью шаблона и обычного суперкласса. Как это

#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 ); };
    };
}
  • 2
    Уже в заголовке вопроса вы находитесь на территории "Я хочу делать плохие вещи". Затем с ** вы говорите: «Да, действительно плохие вещи, и ничто не остановит меня, вы слышите !?». Несоответствие просьбы о помощи с этим, хорошо.
  • 0
    Для этого есть вещи получше, например std::tuple .
Показать ещё 1 комментарий
Теги:
pointers
type-conversion

1 ответ

3
Лучший ответ

Что-то плохое в отношении типов, подобных &*( ClassB* ) за исключением того, что оно не безопасно для типа?

Применение * сопровождаемое & указателем, - это не-op, поэтому просто (ClassB*) должно быть достаточно. Также вы должны использовать C++ -стильные трансляции, в этом случае static_cast<ClassB*>, а не C-style casts, чтобы сделать ваш смысл более явным.

Плохо то, что это не только не безопасно, но и очень запутанно и злоупотребляет языком C++.

Есть ли какая-либо стоимость для этой типизации?

Нет.

Есть ли лучший (более удобный, более безопасный, быстрый) способ хранения указателя на разные объекты другого класса в одном массиве?

Да, лучший способ - отнести общее поведение этих типов к базовому классу (возможно, абстрактно) и объявить ваш массив для хранения указателей на этот базовый класс. Если нет общего поведения, то хранить их все в одном массиве почти всегда не так. Если по какой-то причине вы действительно хотите это сделать, что-то вроде Boost.Any может быть правильным решением.

  • 0
    Ответ на третий вопрос потрясающий.
  • 0
    Благодарю. объявление 3) Если я сделаю массив указателей на общий родительский класс, мне все равно понадобится какой-то тип downcasting (или нет?). Мне кажется, то же самое. Если я считаю, что void * является общим родителем ClassA и ClassB, как и в Java, Object является общим родителем всех классов.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню