从向量返回底层数组

时间:2009-03-03 21:12:01

标签: c++ stl

是否会释放阵列,如果是,那么解决方法是什么?

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 个答案:

答案 0 :(得分:7)

是的 - 一旦你从函数返回它就会被解除分配,因为在堆栈中声明了vecstd::vector析构函数负责释放内存。因为你正在返回一个解除分配的数组的地址,你将开始搞乱释放内存,这是一个很大的禁忌。充其量,你会立即崩溃。在最糟糕的情况下,你会在一个巨大的安全漏洞中默默地成功。

有两种方法可以解决这个问题:(1)返回整个向量by-value,它生成整个向量的副本,或者(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
}

答案 1 :(得分:7)

将你的功能分成两部分。 让你的功能只做一个动作:
1.从地图填充矢量。
2.从矢量创建数组 不要忘记通过const引用传递map。

主要注意事项: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 );
 }  

答案 2 :(得分: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; 
}

答案 3 :(得分: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()做了一些魔术!

答案 4 :(得分:0)

您可以通过引用或新建/删除它来传递它,但是在发布后,您的矢量将在函数返回后被破坏。

答案 5 :(得分:0)

是的,当函数结束时,向量(及它保存的数据存储)将被释放。

为什么要创建矢量?如果你想要一个数组,只需创建&amp;填写其中一个..

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

答案 6 :(得分:0)

是的,当函数退出时,向量将被释放(并且'vec'变量超出范围)。因此,您返回的指针将无效。

另一种方法是在堆上分配数组(使用'new'运算符)并返回该指针,但是调用者有责任'删除'指针,这通常是皱眉。

更好的选择是将shared_ptr返回到您的数组。

答案 7 :(得分: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的方式编写代码;传入要填充的向量,或使用托管/共享指针等(这些选项都已在其他答案中提出)。

答案 8 :(得分:0)

有一个概念调用move constructors,它允许您将(基于堆栈的)对象所拥有的资源的所有权转移到新对象。我听说STLport有一个move_source模板来完成这个

这将来C++0x

在这种情况下,您将返回std::vector<double>而不是double*

答案 9 :(得分:0)

你可以使用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;  
    }  

答案 10 :(得分:0)

有点惊讶没有人提到vector::swap。让调用者传入对向量的引用,该向量的内容将被函数替换:

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

    // construct vec here...

    output.swap(vec); 
}

顺便说一句:“GetArrayFromVector”并传入地图?

答案 11 :(得分:0)

C ++向量具有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