调整我的std :: string时,角色缓冲区会发生什么



背景:

为了使C 中的内存管理更熟悉,我最近决定编写自己的内存管理库。

目前,此库不包括基本池分配器,其中整个足够小,可以在下面进行内联:

PoolAllocator::PoolAllocator (const AllocatorConfig& config)
{
    this->nextAddress = 0;
    this->size = config.AllocatorSize;
    this->memoryArray = new byte[this->size];
}
PoolAllocator::~PoolAllocator ()
{
    delete[] this->memoryArray;
}
void * PoolAllocator::Allocate (size_t size)
{
    void* pointer = &(this->memoryArray[this->nextAddress]);
    this->nextAddress += (unsigned long)size;
    return pointer;
}
void PoolAllocator::Delete (void * object)
{
    this->nextAddress -= sizeof (object);
}
然后,

i然后通过更换分配器来使用分配器来使用分配器(如果存在,否则它只是使用malloc,这就是构造分配器本身时所谓的(。

问题:

在测试此类时,我写了一个简单的应用程序来分配std ::字符串对象,如下所示:

#include <string>
#include <iostream>
#include "PoolAllocator.hpp"
#include "AllocatorConfig.hpp"
PoolAllocator *allocator;
int main (int numArgs, char *args[]) 
{
    AllocatorConfig config = AllocatorConfig ();
    config.AllocatorSize = 2048;
    allocator = new PoolAllocator (config);
    std::string *s = new std::string();
    std::cout << "String object size: " << sizeof (*s) << std::endl;
    *s = "test";
    char *charBuffer = &((*s)[0]);
    std::cout << "First allocate offset: " << (int)((int)&((*s)[0]) - (int)s) << std::endl;
    std::cout << "Original cstring: " << charBuffer << std::endl;
    *s = "thisIsALongStringToTestTheAddress";
    std::cout << "Second allocate offset: " << (int)((int)&((*s)[0]) - (int)s) << std::endl;
    std::cout << "Original cstring: " << charBuffer << std::endl;
    delete allocator;
}
void * operator new(size_t size) 
{
    if (allocator)
    {
        return allocator->Allocate(size);
    }
    else 
    {
        return malloc (size);
    }
}

输出如下:

String object size: 28
First allocate offset: 4
Original cstring: test
Second allocate offset: 36
Original cstring: ,ÀH

首先,这似乎是相当合理的,鉴于字符串由std :: string在内部指向的字符串需要连续,而std :: String确实无法知道它可以简单地扩展到相邻的连续空间。

但是

在替换全局deletedelete[]并连接调试器时,我从未看到任何被调用的证据。我也试图打破free,但这也没有起作用,所以...

问题:

什么是STD :: String使用该内存?我本来会认为它会称为delete[],以便我的分配器可以收回它,但是据我所知,它根本没有发布。它只是在那个地方喷洒堆并坚持下去直到死亡,还是在这里发生了其他事情?

您所看到的是短字符串优化,其中字符串对象将非常小的字符串直接存储在其自身的分配中,因此在重新分配时没有什么可进行的。

请参阅此示例,其中我们有类似的测试,这些结果:

declaration
assignment of 'test'
assignment of 'thisIsALongStringToTestTheAddress'
allocate(34) = 0xb7ac30
assignment of 'thisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddress'
allocate(133) = 0xb7ac60
deallocate(0xb7ac30, 34)
scope end
deallocate(0xb7ac60, 133)

解释这一点:

  • 分配"test"时没有分配。
  • 分配更长的字符串时,有一个分配。
  • 分配更长的字符串以强制重新分配时,还有另一个分配(对于新内容(,然后是(旧内容(的交易,这是我们通常期望的顺序(因此,如果分配失败,则,我们可以在不破坏旧数据的情况下中止操作(。
  • 最后,当字符串被破坏时,我们看到了一个交易。

但是,等等,为什么不看到分配的较长字符串的任何交易呢?好吧,您永远不会delete s; ,因此您的程序以泄漏的分配终止,以使操作系统会清理自身。在delete allocator;之前添加delete s;,您应该看到operator delete中释放的分配。


测试的源代码:

#include <iostream>
#include <string>
template <typename T, typename Allocator = std::allocator<T>>
class AllocatorProxy
{
private:
    Allocator proxy;
public:
    using pointer = typename Allocator::pointer;
    using const_pointer = typename Allocator::const_pointer;
    using value_type = typename Allocator::value_type;
    using size_type = typename Allocator::size_type;
    using difference_type = typename Allocator::difference_type;
    pointer allocate(size_type n)
    {
        pointer p = proxy.allocate(n);
        std::cout << "allocate(" << n << ") = " << static_cast<void *>(p) << 'n';
        return p;
    }
    pointer allocate(size_type n, void const *l)
    {
        pointer p = proxy.allocate(n, l);
        std::cout << "allocate(" << n << ", " << l << ") = " << static_cast<void *>(p) << 'n';
        return p;
    }
    void deallocate(pointer p, size_type n) noexcept
    {
        std::cout << "deallocate(" << static_cast<void *>(p) << ", " << n << ")n";
        proxy.deallocate(p, n);
    }
    size_type max_size()
    {
        return proxy.max_size();
    }
};
using astring = std::basic_string<char, std::char_traits<char>, AllocatorProxy<char>>;
int main() {
    std::cout << "declarationn";
    astring str;
    std::cout << "assignment of 'test'n";
    str.assign("test");
    std::cout << "assignment of 'thisIsALongStringToTestTheAddress'n";
    str.assign("thisIsALongStringToTestTheAddress");
    std::cout << "assignment of 'thisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddress'n";
    str.assign("thisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddressthisIsALongStringToTestTheAddress");
    std::cout << "scope endn";
    return 0;
}

最新更新