开发者

C++ - pointer array to Vector?

double * values; // instead of this,
std::vector<double> values; // I want this.

An API I'm using provides a result as double* poi开发者_如何学Cnter. I want to wrap this with the std::vector<double> type.


You can't wrap an array in a vector in place and expect the vector to operate on that array. The best you can do is give the vector the double* and the number of values, which will have the vector make a copy of every element and put it in itself:

int arrlen = 0;

// pretending my_api takes arrlen by reference and sets it to the length of the array
double* dbl_ptr = my_api(arrlen); 

vector<double> values(dbl_ptr, dbl_ptr + arrlen);

// note that values is *not* using the same memory as dbl_ptr
// so although values[0] == dbl_ptr[0], &values[0] != &dbl_ptr[0]

And also, like Praetorian said, if the API you are using expects you to free the memory after using it, you might be interested in smart pointers. See Praetorian's answer.


Others have suggested that you cannot wrap an array in a vector, but that's simply not true; think about it, a vector has an array as it's underlying data container! I had been attempting this off and on for quite some time before I came up with a workable solution. The caveat is that you have got to zero out the pointers after use in order to avoid double-freeing the memory.

#include <vector>
#include <iostream>

template <class T>
void wrapArrayInVector( T *sourceArray, size_t arraySize, std::vector<T, std::allocator<T> > &targetVector ) {
  typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr =
    (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector);
  vectorPtr->_M_start = sourceArray;
  vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = vectorPtr->_M_start + arraySize;
}

template <class T>
void releaseVectorWrapper( std::vector<T, std::allocator<T> > &targetVector ) {
  typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr =
        (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector);
  vectorPtr->_M_start = vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = NULL;
}

int main() {

  int tests[6] = { 1, 2, 3, 6, 5, 4 };
  std::vector<int> targetVector;
  wrapArrayInVector( tests, 6, targetVector);

  std::cout << std::hex << &tests[0] << ": " << std::dec
            << tests[1] << " " << tests[3] << " " << tests[5] << std::endl;

  std::cout << std::hex << &targetVector[0] << ": " << std::dec
            << targetVector[1] << " " << targetVector[3] << " " << targetVector[5] << std::endl;

  releaseVectorWrapper( targetVector );
}

Alternatively you could just make a class that inherits from vector and nulls out the pointers upon destruction:

template <class T>
class vectorWrapper : public std::vector<T>
{   
public:
  vectorWrapper() {
    this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL;
  }   

  vectorWrapper(T* sourceArray, int arraySize)
  {   
    this->_M_impl _M_start = sourceArray;
    this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize;
  }   

  ~vectorWrapper() {
    this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL;
  }   

  void wrapArray(T* sourceArray, int arraySize)
  {   
    this->_M_impl _M_start = sourceArray;
    this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize;
  }   
};  


const int N = 10; // Number of elements in your array
std::vector<double> vec_values(values, values + N);

This will copy the data in values to a std::vector.


The other answers show how to make a copy of the returned array and create a vector, but assuming the API allocates memory for the array and expects the caller to delete it, you may also want to consider sticking the array into a smart pointer and using it as is.

int numValues;
std::unique_ptr<double[]> values( apiFunction( &numValues ) );

You can still copy this into a vector but if you do the above steps you don't have to worry about deleting the returned array.


If using C++11, you could use a std::vector<std::reference_wrapper<double>>.

#include <functional> // std::reference_wrapper
#include <vector>

#include <iostream>
#include <numeric> // std::iota
#include <random> // std::mt19937
#include <algorithm> // std::shuffle

int main() {
const int N = 10; // Number of elements in your array
double values[N];
std::iota(values, values+N, -4.0);

std::vector<std::reference_wrapper<double>> v(values, values + N);
std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()});

std::cout << "Contents of the array: ";
for(auto i=0; i < N; ++i) std::cout << values[i] << ' ';
std::cout << '\n';

std::cout << "Contents of the array, shuffled: ";
for(auto i: v) std::cout << i << ' ';
std::cout << '\n';

std::cout << "Change values using the vector shuffled\n";
auto j = 44.;
for(double& i: v) i = ++j;

std::cout << "Contents of the array, shuffled: ";
for(auto i: v) std::cout << i << ' ';
std::cout << '\n';
    
std::cout << "Contents of the array: ";
for(auto i=0; i < N; ++i) std::cout << values[i] << ' ';
std::cout << '\n';
}

Possible output:

Contents of the array: -4 -3 -2 -1 0 1 2 3 4 5 
Contents of the array, shuffled: 1 3 -2 0 -3 5 -4 4 -1 2 
Change values using the vector shuffled
Contents of the array, shuffled: 45 46 47 48 49 50 51 52 53 54 
Contents of the array: 51 49 47 53 48 45 54 46 52 50

pro: zero copy

ref: https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper


Use vector iterator constructor

std::vector<int> value_vec (value, value + n); //suppose value has n elements


Thanks to @Ethereal for the nice solution and to make his/her answer more complete:

that code will not compile in visual c++ (maybe will in GCC) because of differences in the std implementation but with some changes, it will work perfectly.

this code tested in Microsoft Visual C++ (VS2015):

#include <iostream>
#include <vector>

template<typename T> std::vector<T> wrapArrayInVector(T* sourceArray, size_t arraySize) {
    std::vector<T> targetVector;
    std::vector<T>::_Mybase* basePtr{ (std::vector<T>::_Mybase*)((void*)&targetVector) };
    basePtr->_Get_data()._Myfirst = sourceArray;
    basePtr->_Get_data()._Mylast = basePtr->_Get_data()._Myend = basePtr->_Get_data()._Myfirst + arraySize;
    return targetVector;
}

int main() {
    int* tests{ new int[3] };
    tests[0] = 100; tests[1] = 200; tests[2] = 300;
    std::vector<int> targetVector{ wrapArrayInVector(tests, 3) };
    std::cout << std::hex << &tests[0] << ": " << std::dec
    << tests[0] << " " << tests[1] << " " << tests[2] << std::endl;
    std::cout << std::hex << &targetVector[0] << ": " << std::dec
    << targetVector[0] << " " << targetVector[1] << " " << targetVector[2] << std::endl;
    std::cin.get();
}

CAUTION:

but you should be noticed you can wrap an array pointer in std::vector just if that pointer is allocated in heap(for example using new keyword) because std::vector is trying to delete the pointer in its destructor, and if array pointer is allocated on the stack it will cause double delete same memory address and it will cause run time error.

so you must not wrap stack allocated array pointer like this

int tests[3];
tests[0] = 100; tests[1] = 200; tests[2] = 300;
std::vector<int> targetVector = wrapArrayInVector(tests, 3);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜