如何获取std :: vector缓冲区的地址最优雅?

时间:2009-08-27 07:45:19

标签: c++ stl vector

我想使用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])看起来很难看。是否有更优雅的方式来实现同样的目标?

11 个答案:

答案 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[]