C++ 在allocator<string> 中销毁元素时会导致双重释放吗?

Cha*_*les 3 c++ std allocator

C++ 分配器。我知道 String 将分配一个new内部实现的块缓冲区,并在析构函数中释放它(调用 delete[])。

我的问题是使用时是否会免费加倍allocator<string>

  1. 首先在字符串析构函数处释放。
  2. 第二个释放缓冲区指向字符串已空闲。

另外,string的buffer地址是否和allocate(n)region相同?

#include <iostream>
#include <memory>
#include <string>

using namespace std;

int main(int argc, char **argv)
{
    const int cnt = 10;
    allocator<string> alloc;
    auto p = alloc.allocate(cnt);

    alloc.construct(p);
    for (int i = 0; i < cnt; ++i)
    {
        cout << p+i << endl; // print buffer address
    }

    alloc.destroy(p); // will it free buffer of string?

    alloc.deallocate(p, cnt); // will it free buffer of string again?

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Ami*_*rsh 6

让我们开始 delete

当你delete成为一个对象时,会发生两件事:

  1. 那个对象的析构函数被称为
  2. 为这个对象分配的内存new被释放回堆

placement newdelete

您可以使用放置new语法在现有内存缓冲区上构造对象:

const char* charString = "Hello, World";
// allocate the required memory
void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
// construct a "Buffer" object on an existing memory block
Buffer* buf = new(mem) Buffer(strlen(charString));

// ...

// destruct the "Buffer" object without releasing the memory
buf->~Buffer();
// deallocate the memory
::operator delete(mem);
Run Code Online (Sandbox Code Playgroud)

当然,对于这个例子,你可以只使用普通的newdelete但它展示了如何将内存分配与其构造分离,以及如何将销毁与释放分离。

例如,如果您管理内存池,并且当对象被破坏而不是返回堆时,可以将内存回收回内存池,则此技术很有用。

标准::分配器

std::allocator给你上述行为-从物体构造和破坏与方法存储器分配和解除分配的分离allocateconstructdestroydeallocate

const int cnt = 10;
allocator<string> alloc;
auto p = alloc.allocate(cnt); // memory is allocated
alloc.construct(p);           // object is constructed on that memory

// ...

alloc.destroy(p);         // object is destructed
alloc.deallocate(p, cnt); // memory is deallocated
Run Code Online (Sandbox Code Playgroud)

请注意,当对象被析构时,std::string析构函数会调用delete其内部分配string,但对象本身占用的内存在上述代码中仅在调用alloc.deallocate.


constructdestroy在 C++17 中被弃用

C ++ 17中声明的方法constructdestroystd::allocator 弃用和C ++ 20使他们过时。因此,从 C++17 开始,您必须在使用placement new分配器分配内存后使用,并在使用分配器释放内存后直接调用析构函数:

const int cnt = 10;
allocator<string> alloc;
auto p = alloc.allocate(cnt); // memory is allocated
// alloc.construct(p);        // deprecated in C++17, obsolete in C++20
new(p) string; // construct the object with placement new

// ...

// alloc.destroy(p);      // deprecated in C++17, obsolete in C++20
p->~string(); // destruct the object by calling the destructor
alloc.deallocate(p, cnt); // memory is deallocated
Run Code Online (Sandbox Code Playgroud)

最后一点:还有一种叫做placement delete它的东西,它不是由程序员直接调用的,而是只有placement new在对象的构造函数抛出异常时才会调用。所以不要混淆。在不释放对象本身占用的内存的情况下就地销毁对象的方法是直接调用其析构函数,如上所示(或者,如果您在 C++17 之前使用 std::allocator,则通过调用 allocator destroy 方法)。


Wil*_*urn 5

当你写这样的东西时:

Foo *foo = new Foo();
Run Code Online (Sandbox Code Playgroud)

发生两件事:

  1. Foo对象分配了一些堆空间。
  2. Foo()构造函数被调用,以this指向新分配的空间。

稍后,您删除该Foo对象:

delete foo;
Run Code Online (Sandbox Code Playgroud)

还有两件事发生了:

  1. 析构函数~Foo()被调用。
  2. 为 Foo 实例分配的内存被释放回堆。

std::allocator班只是让你手动执行这些四个步骤。

如果你有 anallocator<string> alloc并且你调用了alloc.allocatethen alloc.construct,那和做 是一样的new string()。当您调用alloc.destroythen 时alloc.deallocate,这与删除string指针相同。

所以不,不会有任何额外的免费。调用destroy导致string释放它为其缓冲区分配的任何内存,然后调用deallocate释放用于string对象本身的内存。

我没有完全理解你关于该地区的问题。分配用于存储string实例的内存与string为其缓冲区分配的内存无关。