Я хочу хранить указатели на объекты разных классов в одной структуре данных (например, массив, 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 );
}
Теперь вопросы:
&*( ClassB* )
за исключением того, что оно не безопасно для типа? Ах, я понял, что на самом деле не полезно делать это 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 ); };
};
}
Что-то плохое в отношении типов, подобных
&*( ClassB* )
за исключением того, что оно не безопасно для типа?
Применение *
сопровождаемое &
указателем, - это не-op, поэтому просто (ClassB*)
должно быть достаточно. Также вы должны использовать C++ -стильные трансляции, в этом случае static_cast<ClassB*>
, а не C-style casts, чтобы сделать ваш смысл более явным.
Плохо то, что это не только не безопасно, но и очень запутанно и злоупотребляет языком C++.
Есть ли какая-либо стоимость для этой типизации?
Нет.
Есть ли лучший (более удобный, более безопасный, быстрый) способ хранения указателя на разные объекты другого класса в одном массиве?
Да, лучший способ - отнести общее поведение этих типов к базовому классу (возможно, абстрактно) и объявить ваш массив для хранения указателей на этот базовый класс. Если нет общего поведения, то хранить их все в одном массиве почти всегда не так. Если по какой-то причине вы действительно хотите это сделать, что-то вроде Boost.Any может быть правильным решением.
**
вы говорите: «Да, действительно плохие вещи, и ничто не остановит меня, вы слышите !?». Несоответствие просьбы о помощи с этим, хорошо.std::tuple
.