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

为什么std::string分配两次?

  •  3
  • Brandon  · 技术社区  · 10 年前

    我为编写了自定义分配器 std::string std::vector 如下所示:

    #include <cstdint>
    #include <iterator>
    #include <iostream>
    
    template <typename T>
    struct PSAllocator
    {
        typedef std::size_t size_type;
        typedef std::ptrdiff_t difference_type;
        typedef T* pointer;
        typedef const T* const_pointer;
        typedef T& reference;
        typedef const T& const_reference;
        typedef T value_type;
    
        template<typename U>
        struct rebind {typedef PSAllocator<U> other;};
    
        PSAllocator() throw() {};
        PSAllocator(const PSAllocator& other) throw() {};
    
        template<typename U>
        PSAllocator(const PSAllocator<U>& other) throw() {};
    
        template<typename U>
        PSAllocator& operator = (const PSAllocator<U>& other) { return *this; }
        PSAllocator<T>& operator = (const PSAllocator& other) { return *this; }
        ~PSAllocator() {}
    
    
        pointer allocate(size_type n, const void* hint = 0)
        {
            std::int32_t* data_ptr = reinterpret_cast<std::int32_t*>(::operator new(n * sizeof(value_type)));
            std::cout<<"Allocated: "<<&data_ptr[0]<<" of size: "<<n<<"\n";
            return reinterpret_cast<pointer>(&data_ptr[0]);
        }
    
        void deallocate(T* ptr, size_type n)
        {
            std::int32_t* data_ptr = reinterpret_cast<std::int32_t*>(ptr);
            std::cout<<"De-Allocated: "<<&data_ptr[0]<<" of size: "<<n<<"\n";
            ::operator delete(reinterpret_cast<T*>(&data_ptr[0]));
        }
    };
    

    然后我运行了以下测试用例:

    int main()
    {
        typedef std::basic_string<char, std::char_traits<char>, PSAllocator<char>> cstring;
    
        cstring* str = new cstring();
        str->resize(1);
        delete str;
    
        std::cout<<"\n\n\n\n";
    
        typedef std::vector<char, PSAllocator<char>> cvector;
    
        cvector* cv = new cvector();
        cv->resize(1);
        delete cv;
    }
    

    无论出于什么奇怪的原因,它继续打印:

    Allocated: 0x3560a0 of size: 25
    Allocated: 0x3560d0 of size: 26
    De-Allocated: 0x3560a0 of size: 25
    De-Allocated: 0x3560d0 of size: 26
    
    
    
    
    Allocated: 0x351890 of size: 1
    De-Allocated: 0x351890 of size: 1
    

    那么为什么它会为 std::字符串 还有更多的字节?

    我正在Windows 8上使用g++4.8.1 x64 sjlj,来自: http://sourceforge.net/projects/mingwbuilds/ .

    1 回复  |  直到 10 年前
        1
  •  3
  •   MvG    10 年前

    我无法复制双重分配,因为显然我的libstdc++根本没有为空字符串分配任何内容。然而,调整大小确实分配了26个字节,gdb帮助我识别 how they are composed :

    size_type __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep);
                       (     1     + 1) *     1          +     24
    

    所以记忆主要是为了这个 _Rep 表示,它又由以下数据成员组成:

    size_type    _M_length;   // 8 bytes
    size_type    _M_capacity; // 8 bytes
    _Atomic_word _M_refcount; // 4 bytes
    

    我想最后四个字节只是为了对齐,但我可能漏掉了一些数据元素。

    我想这是主要原因 _代表 堆上分配的结构是,它可以在字符串实例之间共享,并且可能还可以避免空字符串,因为我的系统上缺少第一个分配。

    要了解为什么您的实现没有利用这种空字符串优化,请查看 the default constructor 。其实施似乎取决于 _GLIBCXX_FULLY_DYNAMIC_STRING ,这在您的设置中显然是非零的。我不建议直接更改该设置,因为它以下划线开头,因此被认为是私有的。但您可能会发现一些公共设置会影响此值。