我正在努力理解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