代码之家  ›  专栏  ›  技术社区  ›  sharptooth

如何最优雅地获取std::vector buffer的地址?

  •  11
  • sharptooth  · 技术社区  · 15 年前

    我想使用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 回复  |  直到 6 年前
        1
  •  19
  •   wcochran    6 年前

    好吧,你可以删除一组parens:

    &buffer[0]
    

    但这是常见的惯用方法。如果它真的冒犯了你,我想你可以使用一个模板-比如:

    template <typename T> 
    T * StartOf( std::vector <T> & v ) {
        return &v[0];
    }
    
        2
  •  21
  •   wcochran    6 年前

    真奇怪没人知道!!!! 在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 .

        3
  •  18
  •   sbi    15 年前

    实际上,主要问题是 &buffer[0] (注意,没有偏执狂)不是真的很漂亮。(无论如何,这是主观的。我记得发现 buffer.begin(), buffer.end() 一点也不好看,当我第一次学会使用STL的时候。)

    主要问题是,每当 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) 比…更漂亮 &缓冲器〔0〕 由你决定。然而,鉴于此 NULL 应该检查每个指针函数参数,它肯定更安全。

        4
  •  12
  •   Konrad Rudolph    15 年前

    但这 &(buffer[0]) 看起来很丑

    这是正常的方式。但是,您可以省略括号:

    &buffer[0]
    
        5
  •  7
  •   P Shved    15 年前

    不。

        6
  •  4
  •   Michael Krelin - hacker    15 年前

    尝试 &(buffer.front()) 但是不太漂亮:)

        7
  •  3
  •   Kirill V. Lyadvinsky    6 年前

    优雅的方式是改变 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 ) 正如有人最近提出的作为这个答案的编辑,因为在这种情况下你会得到演绎错误。

        8
  •  2
  •   xtofl Adam Rosenfield    15 年前

    它看起来很丑陋的原因是因为你处在简洁干净的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 );
    }
    
        9
  •  2
  •   wrapperapps    6 年前

    对于这些函数,我使用一个实用程序类, SizedPtr<T> 它基本上包含一个指针和一个元素计数。一组转换器函数创建 大小指针 来自不同的输入。所以电话变为:

    vector<TCHAR> foo;
    callFunction(sizedptr(foo));
    

    甚至可以添加一个隐式 std::vector 构造函数到 SizedPtr 但是我想避免这种依赖。

    只有当 callFunction 在你的控制之下。如果您在一个应用程序中使用不同的向量类型,并且希望合并,那么您将很高兴与之合作。如果你通常与 STD::载体 这基本上是毫无意义的。

    粗略地说:

    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对迭代器的操作,但避免了模板感染。

        10
  •  1
  •   Klaim    15 年前

    如前所述,不。

    原因是&buffer[0]是标准保证获取向量缓冲区地址的唯一方法。

        11
  •  1
  •   cibyr    14 年前

    如果你在使用 std::vector 仅仅是为了它的raii属性(这样它就可以为您释放内存),并且您实际上不需要它来调整大小或做任何事情,您最好使用 Boost scoped_array

    boost::scoped_array<TCHAR> buffer( new TCHAR[neededLength] );
    callFunction( buffer.get(), neededLength );
    

    scoped_array 会打电话 delete[] 当数组超出范围时。