我想使用std :: vector来动态分配内存。场景是:
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() );
上面的代码有效,但这个&(buffer[0])
看起来很难看。是否有更优雅的方式来实现同样的目标?
答案 0 :(得分:23)
真的很奇怪,没有人知道这个! 在C ++ 11中你可以使用:
buffer.data()
它可以获取向量的地址 我测试了它:
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);
规范here。
答案 1 :(得分:19)
好吧,你可以删除一组parens:
&buffer[0]
但这是常见的,惯用的方式。如果它真的冒犯了你,我想你可以使用一个模板 - 比如:
template <typename T>
T * StartOf( std::vector <T> & v ) {
return &v[0];
}
答案 2 :(得分:19)
实际上,&buffer[0]
的主要问题(注意没有parantheses)并不是说它不是很漂亮。 (无论如何,这是主观的。当我第一次学习使用STL时,我记得发现buffer.begin(), buffer.end()
并不漂亮。)
主要问题是,只要buffer
为空,它就会调用未定义的行为 - 大多数代码都不会检查它。这就是我将这些放入我的工具箱的原因:
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());}
使用这些,您可以将代码编写为
callFunction( begin_ptr(buffer), buffer.size() );
begin_ptr(buffer)
是否比&buffer[0]
更漂亮,由您决定。但是,考虑到应该为每个指针函数参数检查NULL
,它肯定更安全。
答案 3 :(得分:12)
但这个
&(buffer[0])
看起来很丑陋
这是正常的方式。但是,您可以省略括号:
&buffer[0]
答案 4 :(得分:7)
没有。
答案 5 :(得分:4)
试试&(buffer.front())
,但这并不是更漂亮:)
答案 6 :(得分:3)
优雅的方法是更改callFunction
或为其编写包装器,如下所示:
// 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() );
你甚至可以为所有这些函数创建包装器(使用不同的类型,而不仅仅是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 ) );
}
类型T将被正确推断(std::vector<sometype>
),您仍然可以写callFunction( buffer.begin(), buffer.end() );
。
请注意,您不能将模板功能声明为最近作为此答案编辑的人提出的void callFunction( typename std::vector<typename T::value_type>::iterator begin_it, typename std::vector<typename T::value_type>::iterator end_it )
,因为在这种情况下您将收到deducion错误。
答案 7 :(得分:2)
它看起来丑陋的原因是因为你处于漂亮而干净的C ++风格代码和漂亮而干净的C风格代码的边缘。 C ++代码使用迭代器,C代码使用指针和大小。
你可以创造一些胶水来规避这些问题:
template< typename at_Container, typename at_Function >
void for_container( at_Container& c, at_Function f ) {
f( &c[0], c.size() );
}
并在客户端代码中调用它。
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 );
}
答案 8 :(得分:2)
对于这些函数,我使用的实用程序类SizedPtr<T>
基本上包含指针和元素计数。一组转换器函数从不同的输入创建SizedPtr<T>
。所以电话会改为:
vector<TCHAR> foo;
callFunction(sizedptr(foo));
甚至可以向std::vector
添加隐式SizedPtr
构造函数,但我想避免这种依赖。
仅当callFunction
由您控制时才有用。如果您在一个应用程序中使用不同的矢量类型并且想要合并,那么很高兴与您合作。如果您通常使用std::vector
,那么它几乎毫无意义。
大致是:
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, ...
}
这背后的想法是将操作 - 操作连续的元素序列 - 与存储(std :: vector)分开。它与STL对迭代器的作用类似,但避免了模板感染。
答案 9 :(得分:1)
如前所述,没有。
原因是&amp; buffer [0]是标准保证获取矢量缓冲区的唯一方法。
答案 10 :(得分:1)
如果你只是为了它的RAII属性使用std::vector
(这样它会为你释放内存),而你实际上并不需要它来调整大小或任何东西,你可能最好使用一个Boost scoped_array
boost::scoped_array<TCHAR> buffer( new TCHAR[neededLength] );
callFunction( buffer.get(), neededLength );
scoped_array
会在数组超出范围时调用delete[]
。