2009-03-03 9 views
3

Le tableau sera-t-il désalloué et si oui, qu'est-ce qu'une solution de contournement?Retourne un tableau sous-jacent à partir du vecteur

double * GetArrayFromVector(std::map<std::string, double> m, char ** names, int count) 
{ 
    if(!names) return 0; 

    std::vector<double> vec(m.size()); 
    for (int i=0; i<count; ++i) 
    { 
     if(!names[i]) return 0; 
     std::map<std::string, double>::iterator iter=m.find(name[i]); 
     if(iter!=m.end()) 
      vec.push_back(iter->second); 
     else 
     return 0; 
    } 

    return &vec[0]; 
} 

Merci beaucoup

+0

Que avez-vous besoin à la fin? "double *" ou "std :: vector"? –

+0

J'ai mis à jour ma réponse pour répondre à vos besoins. –

Répondre

7

Divisez votre fonction sur deux. Faites vos fonctions en une seule action:
1. Remplissez le vecteur de la carte.
2. créer un tableau à partir d'un vecteur.
N'oubliez pas de passer la carte par référence const.

Remarque principale: l'appelant de GetArrayFromVector est responsable de la désallocation de la mémoire.

void FillVector(const std::map<std::string, double>& m, 
        std::vector<double>& v, 
        char ** names, 
        int count) 
{ 
     ....... 
} 

double* createArray(const std::vector<double>& v) 
{ 
    double* result = new double [v.size()]; 

    memcpy(result, &v.front(), v.size() * sizeof(double)); 

    return result; 
} 

// and finally your function 

double* GetArrayFromVector(const std::map<std::string, double>& m, 
          char ** names, 
          int count) 
{ 
     std::vector<double> v; 
     FillVector(m, v, names, count); 

     return CreateArray(v); 
} 
+0

Une bonne suggestion. Cependant, j'ai, à la fin, besoin d'un tableau de double. Par conséquent, aucun vecteur n'est nécessaire. vous pouvez éditer votre réponse pour refléter cela –

+0

@Sasha: done. Je n'ai pas testé cela. –

+0

sympa. Je vous remercie –

4

Oui, le tableau sera désallouée.

Modifier la fonction:

double * GetArrayFromVector(std::map<std::string, double> m, vector<double> &vec, char ** names, int count) 
{ 
    vec.clear(); 
    vec.reserve(m.size()); 

    for (int i=0; i<count; ++i) 
    { 
     if(!names[i]) return 0; 

     std::map<std::string, double>::iterator iter=m.find(name[i]); 
     if(iter!=m.end()) 
      vec.push_back(iter->second); 
     else 
      return 0; 
    } 

    return &vec[0]; 
} 

Ou bien utiliser boost::shared_array (aussi, regardez boost::scoped_array)

boost::shared_array<double> GetArrayFromVector(std::map<std::string, double> m, char ** names, int count) 
{ 
    boost::shared_array<double> vec(new double[m.size()]); 

    for (int i=0; i<count; ++i) 
    { 
     if(!names[i]) return boost::shared_array<double>(); 

     std::map<std::string, double>::iterator iter=m.find(name[i]); 
     if(iter!=m.end()) 
      vec[i] = iter->second; 
     else 
      return boost::shared_array<double>(); 
    } 

    return vec; 
} 
+0

Je suis un fervent partisan de l'idée de shared_array. J'irais même plus loin en utilisant scoped_array et en forçant un swap. –

+0

scoped_array est un peu plus difficile à utiliser, mais est probablement un meilleur ajustement dans cette situation (bien que cela puisse dépendre de la façon dont la valeur de retour est utilisée) – Eclipse

+0

Guys, c'est une surcharge pour ce problème .... merci pour l'idée –

0

Vous pouvez le transmettre par référence ou nouvelle/supprimer, mais comme posté votre le vecteur sera détruit après le retour de la fonction.

0

Oui, le vecteur (et le magasin de données qu'il contient) sera désalloué à la fin de la fonction.

Pourquoi créez-vous un vecteur? Si vous voulez un tableau, il suffit de créer & remplir un de ces ..

double * GetArrayFromVector(std::map<std::string, double> m, char * names[], int count) 
{ 
    if(!names) return 0; 

    double* vec = new double[m.size()]; 
    int j = 0; 
    for (int i=0; i<count; ++i) 
    { 
     if(!names[i]) return 0; 
     std::map<std::string, double>::iterator iter=m.find(name[i]); 
     if(iter!=m.end()) 
      vec[j++] =iter->second; 
     else 
     return 0; 
    } 

    return vec; 
} 
+0

vous n'avez pas besoin de j là-bas. –

+0

Vous avez raison. Je ne faisais pas attention au code, et je pensais qu'il ignorait les noms nuls au lieu d'abandonner la fonction. –

7

Oui - il est désallouée dès votre retour de la fonction, car vec est déclarée sur la pile. Le destructeur std::vector prend soin de libérer la mémoire. Puisque vous renvoyez l'adresse d'un tableau désalloué, vous allez commencer à déconner avec la mémoire désallouée, ce qui est un gros non-non. Au mieux, vous vous écraserez immédiatement. Au pire, vous réussirez silencieusement avec un gros trou de sécurité béant.

Il existe deux façons de résoudre ce problème: (1) renvoyer la valeur de référence vectorielle entière, qui effectue une copie du vecteur entier, ou (2) renvoyer le vecteur via un paramètre de référence.

Solution 1:

std::vector<double> GetArrayFromVector(...) 
{ 
    ... 
    return vec; // make copy of entire vec, probably not a good idea 
} 

Solution 2:

void GetArrayFromVector(..., std::vector<double> & vec) 
{ 
    // compute result, store it in vec 
} 
+0

+1. Grande réponse simple. –

0

Oui le vecteur sera désallouée lorsque les sorties de fonction (et la variable 'VEC' est hors de portée). Le pointeur que vous renvoyez sera donc invalide. Une alternative est d'allouer le tableau sur le tas (en utilisant l'opérateur 'new') et de renvoyer ce pointeur à la place, mais il sera de la responsabilité de l'appelant de 'supprimer' le pointeur, qui est habituellement désapprouvé.

Une meilleure alternative est de renvoyer un shared_ptr à votre tableau.

+0

shared_ptr ne fonctionnera pas avec les tableaux, puisqu'ils appellent delete, pas delete [] en cas de destruction. – Eclipse

2

vec est une variable locale. Sa portée est limitée à la fonction GetArrayFromVector() uniquement. Ne jamais retourner l'adresse d'une variable locale.Soit retourner le tableau par valeur:

std::vector<double> GetArrayFromVector(std::map<std::string, double> m, 
         char ** names, int count) 

ou passer une référence au vecteur en tant que paramètres de sortie:

void GetArrayFromVector(std::map<std::string, double> m, 
         char ** names, int count, 
         std::vector<double>& vec) 

ou, passer un itérateur de sortie:

void GetArrayFromVector(std::map<std::string, double> m, 
         char ** names, int count, 
         std::vector<double>::iterator vecIter) 

Le dernier deux nécessiteront une implémentation minutieuse de la définition de la fonction et de l'appel.

De plus, si vous êtes jeu pour un peu plus d'aventure, essayez ceci:

// you'd need to change the value to use when an element is not 
// found in the map to something that suits your needs 
double pred(std::map<char*, double> haystick, char* const needle) { 
    std::map<char*, double>::iterator i = haystick.find(needle); 
    return i != haystick.end() ? i->second : 0; 
} 

int main(int argc, char* argv[]) 
{ 

    std::map<char *, double> m; 
    std::vector<char *> names; 
    std::vector<double> dv; 

    m[ "Sasha" ] = 729.0; 
    m[ "josh" ] = 8154.0; 

    names.push_back("Sasha"); 
    names.push_back("JonSkeet"); 
    names.push_back("josh"); 

    // transform is part of STL's <algorithm> header 
    // it takes a container (actually a range -- [begin(), end()) 
    //     note it is a half-open range -----------^ 
    // as is customary for all STL algorithms, applies the function 
    // or functor specified as the last parameter to each element of 
    // the sequence and writes the result back to another container 
    // specified via the output iterator -- the third argument 
    // 
    // since I have not reserved enough elements for the vector dv 
    // i cannot blindly use it -- i need a back_inserter to coax 
    // transform to push_back() instead of do an insert operation 
    // of course, for vectors, this is costly since reallocations 
    // may happen, but let's leave the performance aside for a while! 
    // 
    // ok, so what about the last parameter, you ask? it has to be an 
    // unary_operation. well, mostly so. but what is it that we want? 
    // we want to take an iterator from the original char* (string) 
    // array and see if there's an entry in the map. if there is one 
    // we retrieve the associated double value and put it in dv; else, 
    // we set a default value of 0 -- change it to whatever pleases you 
    // maybe a std::numeric_limit<double> if it works for you. 
    // 
    // you can create a functor inheriting std::unary_function and pass 
    // it on. that's the easy way out. but what if you already have a 
    // comparator, a C-style find() function? will it work? yes, it will. 
    // but we have to wrap it using the function adaptor std::ptr_fun 
    // to make the compiler happy (after all it wants a unary_function, right?) 
    // 
    // this simple scheme of things works very well, save for a last little 
    // glitch. the comparator actually takes two parameters -- a what to search 
    // and a where to search. and guess what -- the where to search is always 
    // fixed. so that gives us a good oppertunity to fix the first parameter to 
    // our map<char*, double> which is exactly what std::bind1st() does. 
    // surprisingly, now that you've fixed one function, you no longer have a 
    // binary function (one taking two arguments) but an unary one -- which is 
    // just what you'd pass to transform. voila! 
    std::transform(names.begin(), names.end(), std::back_inserter(dv), 
     std::bind1st(std::ptr_fun(pred), m)); 

    std::copy(dv.begin(), dv.end(), 
     std::ostream_iterator<double>(std::cout, "\n")); 

    return 0; 
} 

Quelques liens intéressants:

Consultez également Boost. Ils ont fait de la magie avec bind()!

+0

Très bonne solution: a) J'ai besoin d'un tableau de doubles en sortie b) veuillez expliquer cette ligne: std :: transform (noms.begin(), noms.end(), std :: back_inserter (dv), std :: bind1st (std :: ptr_fun (pred), m)); –

+0

ajouté des commentaires dans le code - être paresseux paresseux;) – dirkgently

+0

Grande explication –

0

Puisque vous connaissez count à l'avant, il n'y a aucun avantage à utiliser un vecteur stl. Vous pouvez simplement faire ceci:

double* GetArrayFromVector(std::map<char*, double> m, char** names, int count) 
{ 
    double* result = new double[count]; 
    for (int i = 0; i < count; ++i) 
    { 
     if(!names[i]) 
     { 
      delete[] result; 
      return 0; 
     } 
     map<std::string, double>::iterator iter = m.find(name[i]); 
     if(iter != m.end()) 
     { 
      result[i] = iter->second; 
     } 
     else 
     { 
      delete[] result; 
      return 0; 
     } 
    } 
    return result; 
} 

Notez que vous passez la propriété du tableau alloué à l'appelant. Personnellement, j'essaierais d'écrire le code d'une manière qui observe le RAII principle; soit passer dans un vecteur à peupler, soit utiliser un pointeur géré/partagé, etc. (ces deux options ont été suggérées dans d'autres réponses).

+0

Cependant, vous venez de supprimer tout espoir d'avoir un code de sécurité exceptionnelle. La sagesse C++ actuelle évite les pointeurs bruts dans la mesure du possible. – Eclipse

+0

facilement erreur sujette, car je peux manquer une suppression. Sinon, ça fonctionne. thx –

+0

D'accord avec les deux commentaires. Personnellement, je préfère passer dans un vecteur pour peupler, ou retourner un pointeur géré d'une certaine sorte. – zweiterlinde

0

Il y a un appel concept move constructors qui vous permettra de transférer la propriété d'une ressource détenue par un objet (basée sur la pile) à un nouvel objet. J'ai entendu que STLport a un modèle de move_source pour accomplir ceci

Cela viendra à C++0x. Dans ce cas, vous devez renvoyer std::vector<double> au lieu de double*.

0

Vous pouvez utiliser le pointeur intelligent std :: auto_ptr (mais le passage de la référence vectorielle à votre fonction est une meilleure solution).

Exemple de std :: auto_ptr:


    std::auto_ptr< std::vector<int> > getArray(int& count){ 
     std::auto_ptr< std::vector<int> > vec(new std::vector<int>()); 
      vec->push_back(10); 
      vec->push_back(12); 
      vec->push_back(14); 
      vec->push_back(16); 

      count = vec->size(); 
      return vec; 
     } 

    int main(){ 
     int size = 0; 
     std::auto_ptr< std::vector<int> > autoPtrVec = getArray(size); 
     int* ptr = &(*autoPtrVec)[0]; 

     std::cout << "Size: " << size << std::endl; 
     for(int i=0; i<size; i++){ 
      std::cout << "[" << i << "]=" << ptr[i] <<  std::endl; 
     } 

     return 0; 
    } 
0

Kinda surpris personne n'a mentionné vector::swap. Avoir le passage de l'appelant dans une référence à un vecteur qui aura son contenu remplacé par la fonction:

void GetArrayFromVector(std::vector<double>& output, ...) 
{ 
    std::vector<double> vec(m.size()); 

    // construct vec here... 

    output.swap(vec); 
} 

BTW: « GetArrayFromVector » et vous passez une carte?

0

Les vecteurs C++ ont un data()method qui renvoie un pointeur vers le tableau sous-jacent.

// vector::data 
#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<int> myvector (5); 

    int* p = myvector.data(); 

    *p = 10; 
    ++p; 
    *p = 20; 
    p[2] = 100; 

    std::cout << "myvector contains:"; 
    for (unsigned i=0; i<myvector.size(); ++i) 
    std::cout << ' ' << myvector[i]; 
    std::cout << '\n'; 

    return 0; 
} 

qui sort

myvector contains: 10 20 0 100 0 
Questions connexes