开发者

c++ vector construct with given memory

开发者 https://www.devze.com 2023-02-07 09:03 出处:网络
I\'d like to use a std::vector to control a given piece of memory. First of all I\'m pretty sure this isn\'t good practice, but curiosity has the better of me and I\'d like to know how to do this anyw

I'd like to use a std::vector to control a given piece of memory. First of all I'm pretty sure this isn't good practice, but curiosity has the better of me and I'd like to know how to do this anyway.

The problem I have is a method like this:

vector<float> getRow(unsigned long rowIndex)
{
    float* row = _m->getRow(rowIndex); // row is now a piece of memory (of a known size) that I control
    vector<float> returnValue(row, row+_m->cols()); // construct a new vec from this data
    delete [] row; // delete the original memory 
    return returnValue; // return the new vector 
}

_m is a DLL interface class which returns an array of float which is the callers responsibility to delete. So I'd like to wrap this in a vector and return that to the user.... but this implementation allocates new memory for the vector, copies it, and then deletes the returned memory, then returns the vector.

What I'd like to do is to straight up tell the new vector that it has full control over this block of memory so when it gets deleted that memory gets cleaned up.

UPDATE: The original motivation for thi开发者_StackOverflow中文版s (memory returned from a DLL) has been fairly firmly squashed by a number of responders :) However, I'd love to know the answer to the question anyway... Is there a way to construct a std::vector using a given chunk of pre-allocated memory T* array, and the size of this memory?


The obvious answer is to use a custom allocator, however you might find that is really quite a heavyweight solution for what you need. If you want to do it, the simplest way is to take the allocator defined (as the default scond template argument to vector<>) by the implementation, copy that and make it work as required.

Another solution might be to define a template specialisation of vector, define as much of the interface as you actually need and implement the memory customisation.

Finally, how about defining your own container with a conforming STL interface, defining random access iterators etc. This might be quite easy given that underlying array will map nicely to vector<>, and pointers into it will map to iterators.

Comment on UPDATE: "Is there a way to construct a std::vector using a given chunk of pre-allocated memory T* array, and the size of this memory?"

Surely the simple answer here is "No". Provided you want the result to be a vector<>, then it has to support growing as required, such as through the reserve() method, and that will not be possible for a given fixed allocation. So the real question is really: what exactly do you want to achieve? Something that can be used like vector<>, or something that really does have to in some sense be a vector, and if so, what is that sense?


Vector's default allocator doesn't provide this type of access to its internals. You could do it with your own allocator (vector's second template parameter), but that would change the type of the vector.

It would be much easier if you could write directly into the vector:

vector<float> getRow(unsigned long rowIndex) {
  vector<float> row (_m->cols());
  _m->getRow(rowIndex, &row[0]);  // writes _m->cols() values into &row[0]
  return row;
}

Note that &row[0] is a float* and it is guaranteed for vector to store items contiguously.


The most important thing to know here is that different DLL/Modules have different Heaps. This means that any memory that is allocated from a DLL needs to be deleted from that DLL (it's not just a matter of compiler version or delete vs delete[] or whatever). DO NOT PASS MEMORY MANAGEMENT RESPONSIBILITY ACROSS A DLL BOUNDARY. This includes creating a std::vector in a dll and returning it. But it also includes passing a std::vector to the DLL to be filled by the DLL; such an operation is unsafe since you don't know for sure that the std::vector will not try a resize of some kind while it is being filled with values.

There are two options:

  • Define your own allocator for the std::vector class that uses an allocation function that is guaranteed to reside in the DLL/Module from which the vector was created. This can easily be done with dynamic binding (that is, make the allocator class call some virtual function). Since dynamic binding will look-up in the vtable for the function call, it is guaranteed that it will fall in the code from the DLL/Module that originally created it.

  • Don't pass the vector object to or from the DLL. You can use, for example, a function getRowBegin() and getRowEnd() that return iterators (i.e. pointers) in the row array (if it is contiguous), and let the user std::copy that into its own, local std::vector object. You could also do it the other way around, pass the iterators begin() and end() to a function like fillRowInto(begin, end).

This problem is very real, although many people neglect it without knowing. Don't underestimate it. I have personally suffered silent bugs related to this issue and it wasn't pretty! It took me months to resolve it.

I have checked in the source code, and boost::shared_ptr and boost::shared_array use dynamic binding (first option above) to deal with this.. however, they are not guaranteed to be binary compatible. Still, this could be a slightly better option (usually binary compatibility is a much lesser problem than memory management across modules).


Your best bet is probably a std::vector<shared_ptr<MatrixCelType>>.

Lots more details in this thread.


If you're trying to change where/how the vector allocates/reallocates/deallocates memory, the allocator template parameter of the vector class is what you're looking for.

If you're simply trying to avoid the overhead of construction, copy construction, assignment, and destruction, then allow the user to instantiate the vector, then pass it to your function by reference. The user is then responsible for construction and destruction.

It sounds like what you're looking for is a form of smart pointer. One that deletes what it points to when it's destroyed. Look into the Boost libraries or roll your own in that case.


The Boost.SmartPtr library contains a whole lot of interesting classes, some of which are dedicated to handle arrays.

For example, behold scoped_array:

int main(int argc, char* argv[])
{
  boost::scoped_array<float> array(_m->getRow(atoi(argv[1])));
  return 0;
}

The issue, of course, is that scoped_array cannot be copied, so if you really want a std::vector<float>, @Fred Nurk's is probably the best you can get.

In the ideal case you'd want the equivalent to unique_ptr but in array form, however I don't think it's part of the standard.

0

精彩评论

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