2009-08-11 2 views
1

Je réalise un projet en convertissant du code Pascal (Delphi) en C++ et j'aimerais écrire une fonction qui est à peu près équivalente au Pascal "SetLength" méthode. Cela prend une référence à un tableau dynamique, ainsi qu'une longueur et alloue la mémoire et renvoie la référence.Une méthode générique pour définir la longueur d'un tableau dynamique de type arbitraire en C++

En C++, je pensais à quelque chose le long des lignes de

void* setlength(void* pp, int array_size, int pointer_size, int target_size, ....) { 
     void * p; 
     // Code to allocate memory here via malloc/new 
     // something like: p = reinterpret_cast<typeid(pp)>(p); 
     //     p=(target_size) malloc(array_size); 
     return p; 
} 

Ma question est la suivante: est-il un moyen de passer le type de pointeur à une fonction comme celui-ci et d'allouer avec succès la mémoire (peut-être via un paramètre de typeid?)? Puis-je utiliser

<reinterpret_cast> 

en quelque sorte? Le but ultime serait quelque chose comme ce qui suit en termes d'utilisation:

float*** p; 
p=setlength(100,sizeof(float***),sizeof(float**),.....); 

class B; 
B** cp; 
cp=setlength(100,sizeof(B**),sizeof(B*),.....); 

Toute aide serait la bienvenue. Je suis conscient que mon code suggéré est tout faux, mais je voulais transmettre l'idée générale. Merci.

Répondre

5

Utilisez std :: vector au lieu des tableaux raw. Ensuite, vous pouvez simplement appeler sa méthode de membre resize().

et faire fonctionner un modèle pour gérer les types arbitraires:

Si vous souhaitez utiliser votre fonction, il pourrait ressembler à ceci:

template <typename T> 
std::vector<T>& setlength(std::vector<T>& v, int new_size) { 
     v.resize(new_size); 
     return v; 
} 

Mais maintenant il est si simple que vous pourriez vouloir éliminez la fonction entièrement et appelez simplement resize pour commencer. Je ne suis pas entièrement sûr de ce que vous essayez de faire avec les triple-pointeurs dans votre exemple, mais il semble que vous ne voulez pas redimensionner, vous voulez initialiser à une certaine taille, ce qui peut être fait avec le constructeur vector:

std::vector<float>v(100); 
1

Pour un tableau multidimensionnel, probablement la meilleure option serait d'utiliser la bibliothèque multi_array de boost:

typedef boost::multi_array<float, 3> array_type; 
array_type p(boost::extents[100][100][100]); // make an 100x100x100 array of floats 
p[1][2][3] = 4.2; 

Cela vous permet de disparaître complètement abstraction de l'allocation et les détails de la mise en place du tableau multidimensionnel. De plus, parce qu'il utilise le stockage linéaire, vous obtenez les avantages d'efficacité du stockage linéaire avec la facilité d'accès des indirections. A défaut, vous avez trois autres options majeures.

Le plus C++ option - y sans utiliser des bibliothèques externes serait d'utiliser un conteneur STL:

std::vector<float **> p; 
p.resize(100); 

Comme multi_array, p sera alors automatiquement libéré quand il est hors de portée. Vous pouvez obtenir les limites vectorielles avec p.size(). Cependant, le vecteur ne traitera qu'une seule dimension pour vous, donc vous finirez par faire des vecteurs imbriqués (ick!).

Vous pouvez également utiliser new directement:

float ***p = new float**[100]; 

Pour désaffecter:

delete [] p; 

Cela a tous les inconvénients de std::vector, plus il ne libérera pas pour vous, et vous pouvez » t obtenir la taille plus tard.

Les trois méthodes ci-dessus lanceront toutes une exception de type std::bad_alloc si elles ne parviennent pas à allouer suffisamment de mémoire.

Enfin, pour être complet, il y a la route C, avec calloc():

float ***p = (float ***)calloc(100, sizeof(*p)); 

Pour gratuit:

free((void*)p); 

Cela vient de C et est un peu plus laid avec tous les moulages. Pour les classes C++, il n'appellera pas non plus les constructeurs pour vous.En outre, il n'y a aucune vérification que le sizeof dans l'argument est compatible avec le cast.

Si calloc() ne parvient pas à allouer de la mémoire, il retournera NULL; vous aurez besoin de vérifier cela et de le gérer.

+1

Je ne les appellerais certainement pas des monstruosités, elles donnent une manière beaucoup plus intuitive d'accéder à des structures de données multidimensionnelles. Cela étant dit, vous pouvez perdre une partie de la performance en comparaison avec la dimension majeure du tableau linéaire. – DeusAduro

+0

C'est ce que j'utilise en ce moment, mais je me demandais s'il y avait un moyen de mettre toutes les vérifications d'exception en un seul endroit sur l'allocation. Mon application utilise beaucoup de tableaux 3D où l'indirection triple est (hélas) nécessaire –

+0

beaucoup plus intuitive que quoi? – jalf

0

le nouvel opérateur lui-même est essentiellement ce que vous demandez, à l'exception que d'allouer de façon appropriée pour les pointeurs triples doubles/vous devez faire quelque chose le long des lignes suivantes:

float** data = new float*[size_of_dimension_1]; 
for (size_t i=0 ; i<size_of_dimension_1 ; ++i) 
    data[i] = new float[size_of_dimension_2]; 

... 

// to delete: 
for (size_t i=0 ; i<size_of_dimension_1 ; ++i) 
    delete [] data[i]; 
delete [] data; 

Editer: Je suggère d'utiliser l'une des nombreuses bibliothèques mathématiques/matricielles C++ disponibles. Je suggérerais uBlas.

+0

Oui, voici comment je On les a fait, mais pour chaque type une nouvelle boucle doit être écrite (ie une pour un tableau 3D de flottants, une pour un tableau 3D d'objets de classe ...). Je suis un peu paralysé par le fait que je fais un travail de traduction, plutôt que de zéro. –

2

Si vous vouliez faire littéralement, vous feriez comme ceci:

template <typename T> 
T* SetLength(T* arr, size_t len) { 
    return static_cast<T*>(realloc(arr, sizeof(T) * len)); 
} 

Notez que le tableau doit ont été attribués avec malloc ou calloc. Notez également que cela ne redimensionne pas réellement la mémoire: elle libère la mémoire et réalloue la mémoire de la taille appropriée. S'il y avait d'autres pointeurs sur le tableau transmis, ils seront ensuite invalides.

Vous êtes vraiment mieux d'utiliser une solution C++ plus idiomatique, comme std::vector.

0

Pour ce faire, le C++ façon:

1) Comme indiqué jalf, préférez std :: vecteur si vous pouvez
2) Ne pas faire void * p. Préférez plutôt faire de votre fonction un modèle de type T.