вернуть базовый массив из вектора - PullRequest
3 голосов
/ 04 марта 2009

Будет ли освобожден массив, и если да, то каков обходной путь?

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

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

Ответы [ 12 ]

7 голосов
/ 04 марта 2009

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

Главное примечание: вызывающий 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 );
 }  
7 голосов
/ 04 марта 2009

Да - оно освобождается, как только вы возвращаетесь из функции, потому что 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
}
4 голосов
/ 04 марта 2009

Да, массив будет освобожден.

Измените функцию на:

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; 
}
2 голосов
/ 04 марта 2009

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 голосов
/ 20 мая 2013

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

// 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
0 голосов
/ 04 марта 2009

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

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

    // construct vec here...

    output.swap(vec); 
}

Кстати: "GetArrayFromVector", а вы переходите на карту?

0 голосов
/ 04 марта 2009

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

Пример 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 голосов
/ 04 марта 2009

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

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

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

0 голосов
/ 04 марта 2009

Поскольку вы знаете 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 ; либо передайте вектор для заполнения, либо используйте управляемый / общий указатель и т. д. (оба эти варианта были предложены в других ответах).

0 голосов
/ 04 марта 2009

Да, вектор будет освобожден при выходе из функции (и переменная 'vec' выйдет из области видимости). Поэтому возвращаемый вами указатель будет недействительным.

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

Лучшая альтернатива - вернуть shared_ptr в ваш массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...