2009-03-03 1 views
3

Будет ли выделен массив, и если да, то что такое обходной путь?вернуть базовую матрицу из вектора

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]; 
} 

Большое спасибо

+0

Что вам нужно в конце концов? "double *" или "std :: vector"? –

+0

Я обновил свой ответ, чтобы он соответствовал вашим требованиям. –

ответ

7

Разделите свою функцию на две части. Сделайте свои функции одним действием:
1. заполните вектор с карты.
2. создать массив из вектора.
Не забудьте передать карту по ссылке const.

Основное примечание: вызывающий объект GetArrayFromVector отвечает за освобождение памяти.

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

Хорошее предложение. Тем не менее, я, в конце концов, нуждаюсь в массиве double. Следовательно, вектор не нужен. вы можете отредактировать свой ответ, чтобы отразить это – 2009-03-03 21:44:45

+0

@ Саша: сделано. Я не тестировал это. –

+0

приятно. Спасибо –

4

Да массив будет освобождаться.

Изменения функции:

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]; 
} 

Или использовать boost::shared_array (также, смотрите на 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

Я твердо верю в идею shared_array. Я бы пошел еще дальше, используя scoped_array и заставляя своп. –

+0

scoped_array немного сложнее в использовании, но, вероятно, лучше подходит в этой ситуации (хотя это может зависеть от того, как используется возвращаемое значение) – Eclipse

+0

Ребята, это перебор для этой проблемы .... спасибо за идею – 2009-03-03 21:45:27

0

Вы можете передать его по ссылке или новой/удалить его, но, как писали ваш вектор будет разрушен после возвращения функции.

0

Да, вектор (и хранимое хранилище данных) будет освобожден при завершении функции.

Почему вы создаете вектор? Если вы хотите массив, просто создать & заполнить один из тех ..

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

вам НЕ нужен j. – 2009-03-04 15:53:06

+0

Вы правы. Я не обращал внимания на код и думал, что он пропускает пустые имена вместо того, чтобы прерывать функцию. –

7

Да - это освобождаться как только возвращения из функции, потому что vec объявлена ​​в стеке. Дескриптор std::vector позаботится об освобождении памяти. Поскольку вы возвращаете адрес освобожденного массива, вы начнете возиться с освобожденной памятью, а это большой нет-нет. В лучшем случае вы немедленно столкнетесь. В худшем случае, вы без труда добьетесь успеха с большой зияющей дырой в безопасности.

Есть два способа исправить это: (1) вернуть всю векторную величину, которая делает копию всего вектора, или (2) вернуть вектор через опорный параметр.

Решение 1:

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

Решение 2:

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

+1. Отличный прямой ответ. –

0

Да вектор будет освобождаться при выходе из нее (и 'VEC' переменная выходит из области видимости). Указанный вами указатель будет недействительным.

Альтернативой является выделение массива в куче (с использованием «нового» оператора) и вместо этого возвращает этот указатель, но ответственность за удаление указателя, который обычно недоверчиво, будет заключаться в том, что вызывающий объект отвечает за «удаление» указателя.

Лучшей альтернативой является возвращение shared_ptr в ваш массив.

+0

shared_ptr не будет работать с массивами, поскольку они вызывают удаление, а не удаление [] при уничтожении. – Eclipse

2

vec - локальная переменная. Его объем ограничен только функцией GetArrayFromVector(). Никогда не возвращайте адрес локальной переменной.Либо возвращает массив по значению:

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

или передать ссылку на вектор в качестве выходного параметра:

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

или передать выходной итератор:

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

Последнее два потребуют некоторой тщательной реализации определения функции и вызова.

Кроме того, если вы игра для немного больше приключений, попробуйте следующее:

// 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; 
} 

Некоторые интересные ссылки:

Также проверьте Boost. Они сделали магию с bind()!

+0

Очень хорошее решение: a) Мне нужен массив удвоений в качестве выхода b), пожалуйста, объясните эту строку: std :: transform (names.begin(), names.end(), std :: back_inserter (dv), std :: bind1st (std :: ptr_fun (pred), m)); – 2009-03-04 15:50:20

+0

добавил комментарии в код - быть простым ленивым;) – dirkgently

+0

Отличное объяснение – 2009-03-04 17:51:38

0

Поскольку вы знаете count спереди, нет никакой пользы для использования stl-вектора. Вы могли бы просто сделать это:

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

Обратите внимание, что вы передаете право собственности на выделенный массив на вызывающего. Лично я бы попытался написать код таким образом, который соблюдает RAII principle; либо пройти в векторе, который будет заполнен, либо использовать управляемый/общий указатель и т. д. (оба этих варианта были предложены в других ответах).

+0

Тем не менее, вы просто удалили какую-либо надежду на исключение-безопасный код. Текущая мудрость C++ позволяет избежать необработанных указателей, где это возможно. – Eclipse

+0

легко подвержен ошибкам, так как я могу пропустить удаление. В противном случае он работает. thx – 2009-03-03 21:31:39

+0

Согласитесь с обоими комментариями. Лично я предпочитаю переходить в вектор для заполнения или возвращать управляемый указатель какого-то типа. – zweiterlinde

0

Существует понятие вызова move constructors, что позволит вам передать в собственность ресурса, удерживаемого с помощью (стековой) объекта на новый объект. Я слышал, что у STLport есть шаблон move_source для выполнения этого

Это будет на C++0x.

В этом случае вы должны будете вернуть std::vector<double> вместо double*.

0

Вы можете использовать умный указатель std :: auto_ptr (но передача векторной ссылки на вашу функцию - лучшее решение).

Пример станд :: 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

кинда удивлены никто не упоминается vector::swap. Есть пропуск вызывающего абонента в ссылке на вектор, который будет его содержимое заменяется функцией:

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

    // construct vec here... 

    output.swap(vec); 
} 

КСТАТИ: «GetArrayFromVector» и вы передаете в карте?

0

С ++ векторы имеют data()method, который возвращает указатель на базовый массив.

// 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; 
} 

, который выводит

myvector contains: 10 20 0 100 0