在Visual Studio 2019中是否存在std::pmr::memory_resource的问题?



我正在努力理解c++ 17内存资源(例如内存池)的概念。

我得到了它的一部分工作,但如果我使用的是new_delete_resource()以外的内存资源,我会得到意外的行为。我在Visual Studio 2019上,但也尝试过llvm和Intel 2022编译器。

请看简化的例子:

#include <iostream>
#include <memory_resource>
int
main(int argc, char *argv[])
{
//std::pmr::memory_resource* mr = std::pmr::new_delete_resource(); // works
std::pmr::memory_resource *mr = new std::pmr::unsynchronized_pool_resource(); // does not work
//std::pmr::memory_resource* mr = new std::pmr::monotonic_buffer_resource(1000); // does not work
void* ptr = mr->allocate(sizeof(int));
int *i1 = new (ptr) int;
*i1 = 4711;
ptr = mr->allocate(sizeof(int));
int* i2 = new (ptr) int;
*i2 = 815;
std::cout << *i1 << 'n';
std::cout << *i2 << 'n';
delete i1;
std::cout << *i2 << 'n';
return 0;
}

这是一个bug在VS,即他们的库实现?

TK

我希望在所有三种内存资源中得到相同的结果。

类的用法如下:

#include <iostream>
#include <memory_resource>
class myclass {
public:
int x;
};
int
main(int argc, char *argv[])
{
//std::pmr::memory_resource* mr = std::pmr::new_delete_resource(); // works
std::pmr::memory_resource *mr = new std::pmr::unsynchronized_pool_resource(); // does not work
//std::pmr::memory_resource* mr = new std::pmr::monotonic_buffer_resource(1000); // does not work
void* ptr = mr->allocate(sizeof(myclass));
myclass *c1 = new (ptr) myclass;
c1->x = 4711;
ptr = mr->allocate(sizeof(myclass));
myclass *c2 = new (ptr) myclass;
c2->x = 815;
std::cout << c1->x << 'n';
std::cout << c2->x << 'n';
c1->~myclass();
mr->deallocate(c1, sizeof(myclass));
std::cout << c2->x << 'n';
return 0;
}

问题是

delete i1;

当您使用std::pmr::new_delete_resource时,方法allocate使用::operator new,方法deallocate使用::operator delete,根据cppreference。由于您不调用deallocate,而是通过delete表达式调用delete操作符,因此i1::operator delete'd(动态内存释放)精确地释放一次。

但是当您使用其他内存资源时,allocate从预分配的缓冲区中分配内存(具体机制取决于特定的内存资源),并且返回的指针不是通过动态内存分配获得的。因此,删除它会导致未定义的行为。

因此,要正确处理所有内存资源的释放,只需使用它们的deallocate方法:
mr->deallocate(i1, sizeof(int));

请注意,对于std::pmr::monotonic_buffer_resource,这个操作是无操作的,而对于std::pmr::unsynchronized_pool_resource,它是不需要的(因为它将在销毁过程中从它管理的上游资源内存中获得释放),但如果实现明智地使用它,例如,释放一些池/块,所有deallocate"未注册"的块,则可能是有益的。

还要注意,当对象在分配的内存中通过放置new手工调用构造函数创建时,它们应该通过调用析构函数手工销毁,这可以对所有类型(包括内置类型,您不能直接使用析构函数语法调用,例如~int())使用std::destroy_at:

std::destroy_at(i1); // or c1; no-op for trivially destructible types
mr->deallocate(i1, sizeof(int)); // or c1

最新更新