可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to use std::vector for dynamically allocating memory. The scenario is:
int neededLength = computeLength(); // some logic here
// this will allocate the buffer
std::vector<TCHAR> buffer( neededLength );
// call a function that accepts TCHAR* and the number of elements
callFunction( &(buffer[0]), buffer.size() );
The code above works, but this &(buffer[0])
looks ugly. Is there a more elegant way to achieve the same?
回答1:
Well, you can remove one set of parens:
&buffer[0]
but that is the common, idiomatic way of doing it. If it really offends you, I suppose you could use a template - something like:
template <typename T>
T * StartOf( std::vector <T> & v ) {
return &v[0];
}
回答2:
It's really odd that nobody know this!!!
in C++11 you could use:
buffer.data()
it could get the address of the vector
I have test it:
vector<char>buffer;
buffer.push_back('w');
buffer.push_back('h');
buffer.push_back('a');
buffer.push_back('t');
buffer.push_back('\0');
char buf2[10];
memcpy(buf2,buffer.data(),10);
Specification here.
回答3:
Actually, the main problem with &buffer[0]
(note the absence of parantheses) isn't that it isn't really pretty. (That's subjective anyway. I remember finding buffer.begin(), buffer.end()
not pretty at all, when I first learned to use the STL.)
The main problem is that it invokes undefined behavior whenever buffer
is empty -- and most code never checks for that. That's why I put these into my toolbox:
template <class T, class TAl>
inline T* begin_ptr(std::vector<T,TAl>& v)
{return v.empty() ? NULL : &v[0];}
template <class T, class TAl>
inline const T* begin_ptr(const std::vector<T,TAl>& v)
{return v.empty() ? NULL : &v[0];}
template <class T, class TAl>
inline T* end_ptr(std::vector<T,TAl>& v)
{return v.empty() ? NULL : (begin_ptr(v) + v.size());}
template <class T, class TAl>
inline const T* end_ptr(const std::vector<T,TAl>& v)
{return v.empty() ? NULL : (begin_ptr(v) + v.size());}
Using these, you can write your code as
callFunction( begin_ptr(buffer), buffer.size() );
Whether begin_ptr(buffer)
is prettier than &buffer[0]
is left for you to decide. However, given that NULL
should be checked for every pointer function argument, it definitely is more safe.
回答4:
but this &(buffer[0])
looks ugly
It’s the normal way. You can omit the parentheses, though:
&buffer[0]
回答5:
回答6:
Try &(buffer.front())
, but it's not much prettier :)
回答7:
Elegant way would be to change callFunction
or to write wrapper for it as follows:
// legacy function
void callFunction( TCHAR* buf, int buf_size)
{
// some code
}
// helpful template
void callFunction( std::vector<TCHAR>::iterator begin_it, std::vector<TCHAR>::iterator end_it )
{
callFunction( &*begin_it, std::distance( begin_it, end_it ) );
}
// somewhere in the code
int neededLength = computeLength();
std::vector<TCHAR> buffer( neededLength );
callFunction( buffer.begin(), buffer.end() );
You could even make wrapper for all such functions (with different types, not only TCHAR):
template<typename T>
void callFunction( T begin_it, typename std::vector<typename T::value_type>::iterator end_it )
{
callFunction( &*begin_it, std::distance( begin_it, end_it ) );
}
Type T will be properly deduced (as std::vector<sometype>
) and you'll be able still write callFunction( buffer.begin(), buffer.end() );
.
Note that you cannot declare template function as void callFunction( typename std::vector<typename T::value_type>::iterator begin_it, typename std::vector<typename T::value_type>::iterator end_it )
as someone proposed recently as an edit to this answer, because in that case you will get the deducion error.
回答8:
The reason it looks ugly is because you're at the borderline of nice and clean C++ style code and nice and clean C style code. The C++ code uses iterators, the C code uses pointers and sizes.
You could create some glue to circumvent these problems:
template< typename at_Container, typename at_Function >
void for_container( at_Container& c, at_Function f ) {
f( &c[0], c.size() );
}
and call it in the client code.
void afunction( int* p, size_t n ) {
for( int* p = ap; p != ap+n; ++p ) {
printf( "%d ", *p );
}
}
void clientcode() {
std::vector<int> ints(30,3);
for_container( ints, afunction );
}
回答9:
For functions like these, I use a utility class, SizedPtr<T>
that basically holds a pointer and an element count. A set of converter functions creates the SizedPtr<T>
from different inputs. So the call changes to:
vector<TCHAR> foo;
callFunction(sizedptr(foo));
One could even add an implicit std::vector
constructor to SizedPtr
, but I wanted to avoid this dependency.
This helps only if callFunction
is under your control. It is a pleasure to work with, if you work with different vector types in one application and you want to consolidate. If you generally work with std::vector
, it's mostly pointless.
Roughly:
template<typename T>
class SizedPtr
{
T * m_ptr;
size_t m_size;
public:
SizedPtr(T* p, size_t size) : ... {}
T * ptr() { return m_ptr; }
size_t size() const { return m_size; }
// index access, STL container interface, Sub-Sequence, ...
}
The idea behind this is to separate the operation - manipulating a contiguous sequence of elements - from the storage (std::vector). It's similar to what STL does with iterators, but avoids template infection.
回答10:
As already said, no.
The reason is that &buffer[0] is the only way guarantied by the standard to get the adresse of the vector buffer.
回答11:
If you're using std::vector
just for its RAII properties (so that it will free the memory for you), and you don't actually need it to resize or anything, you might be better off using a Boost scoped_array
boost::scoped_array<TCHAR> buffer( new TCHAR[neededLength] );
callFunction( buffer.get(), neededLength );
scoped_array
will call delete[]
on the array when it goes out of scope.