c++ Array passing dilemma
I am writing a function that takes a string, string pointer and an int. The function splits the string based on a set of rules and puts each token into an array. I need to return the array out of the function with the number of elements in the int variable etc. I am stuck as to how I return the array as I can not use auto other wise it is destroyed and I am reluctant to use new as I feel this is patchy.
I have other ideas on how to go about this but would like t开发者_JAVA技巧o see how other people go about this first. I could also be wrong and it could be possible to pass an auto out of an array. I can also not use vectors so there goes a copy constructor.
Vector can not be used as this was a challenge set out to me and I was asked not use to templates.
This is more of a C than a C++ question given those restrictions.
The common C pattern for returning an array is actually to get the caller to pass in an array to fill. This lets the caller decide on the allocation (and hence deallocation).
Your function prototype would look like
int Function(string str1, string_ptr str2, int n, int* pOutArray, int cOutArray);
Where the function returns the number of elements written the pOutArray.
In the implementation you put in handling for pOutArray being NULL, in which case you just count the number of elements, and return that. This lets you call the function in one of several ways depending on your needs :-
int out[5]={0};
int cFilled = Function(s1,s2,x,out,_countof(out));
// Further code can use up to 5<cFilled elements from the array.
or,
int cElt = Function(s1,s2,x,NULL,0);
int* pOut = malloc(sizeof(int)*cElt);
Function(s1,s2,x,pOut,cElt);
// pOut now contains exactly the number of elements extracted.
free(pOut);
The natural choice would of course be to pass the result as std::vector<std::string>
. If you don't want to use this approach there are two options:
- Let the client of the code supply the storage of the result, in this case it would be a good idea that the client passes the size of the supplied storage as well.
- Allocate space for the result inside with
new
, I think this is a better, more robust choice. You will of course have to make sure that the client later deletes the memory with the correct version ofdelete
or provide a special mechanism to deallocate the memory.
If you are returning a fixed-size array, you can use boost::array. However, if you don't want to add the Boost dependency, the simple solution is to create a data structure with the fixed-size array, as in:
template<typename T, std::size_t SIZE> struct array_wrapper { T array[SIZE]; };
If the size can change, then using std::vector
really does make the most sense. Keep in mind that returning the wrapper class (as given above) will still result in a copy being created (albeit, by the compiler). So, you really don't get any savings by passing the large result by value rather than by constructing it on the heap and passing around the pointers to the allocated data, so your requirements don't make sense.
Pass in 'char &** ' and 'int &* ' parameters and allocate the tokens there, leaving the responsibility of releasing the memory to the caller. Not very nice by modern standards, but works.
If you choose not to use the standard methods of managing memory (ie std::vector), then you will have to make a choice.
The three basic options are:
- Caller manages memory. Caller passes in a block of memory (and probably, an int specifying how big that array is). You fill that array. Function doesn't need to do memory allocation.
- Callee manages memory. The function allocates a big enough block, fills it, then returns a pointer to it.
- Collaboration. Caller passes a call-back function, callee uses that function to allocate the block.
Option (2) is complicated by the fact that someone needs to deallocate the memory. As part of the API, you need to specify whether the function somehow clears that block up later (and when), or whether the caller is responsible.
Option (3) is probably overkill - you need to write a function just to call the main function.
Option (1) is therefore your best best (if you can't use vectors). It's a pretty standard pattern - for example, the low-level Windows APIs use that pattern.
Basically, memory management must be documented as part of your API.
精彩评论