[C++17] 内存池资源(polymorphic memory resource, pmr)

例子1 hello world

#include <iostream>
#include <string>
#include <vector>
#include <memory_resource>
#include <unordered_map>


void foo1(){
    unsigned char buff[1024]{};

    // 用栈内存创建一个内存资源管理器
    std::pmr::monotonic_buffer_resource pool(buff,1024,std::pmr::null_memory_resource());

    // 使用内存池创建两个内存池string
    std::pmr::string s1{"my string", &pool};
    std::pmr::string s2{"my string", &pool};

    // 创建一个内存池vec
    std::pmr::vector<std::pmr::string> vec{&pool};

    // 创建一个内存池map
    std::pmr::unordered_map<int,std::pmr::string> map{&pool};
}

int main() {
    foo1();
}


// null_memory_resource()的作用:
// 通过传递null_memory_resource()作为备选内存资源,
// 我们可以确保任何尝试分配 更多内存的行为都会抛出异常而不是在堆上分配内存。
// 就是说用了这个参数,可以保证不会发生堆分配.




例子2 从结果对比预期

从输出结果可以看到,
vec和map内部的元素是在池子中的,
但是vec和map本身不在.

#include <iostream>
#include <string>
#include <vector>
#include <memory_resource>
#include <unordered_map>

//#define string                      std::pmr::string
//#define vector(T)                   std::pmr::vector<T>
//#define unordered_map(T1, T2)       std::pmr::unordered_map<T1,T2>
//#define monotonic_buffer_resource   std::pmr::monotonic_buffer_resource
//#define null_memory_resource        std::pmr::null_memory_resource

bool isAddressInBuff(void *addr, void *start, std::size_t size);

void printResult(const std::pmr::string &name, void *addr, bool isInBuff);

void foo2();

int main() {
    foo2();
}

void foo2() {
    unsigned char buff[1024]{};
    std::pmr::monotonic_buffer_resource pool(buff, 1024,
                                             std::pmr::null_memory_resource());
    std::pmr::string s1{"1111 ", &pool};
    std::pmr::string s2{"2222 ", &pool};
    std::pmr::string s3{"3333 ", &pool};
    std::pmr::string &s4 = *new std::pmr::string{"4444 ", &pool};
    std::pmr::string &s5 = *new std::pmr::string{"5555 ", &pool};
    std::pmr::string &s6 = *new std::pmr::string{"6666 ", &pool};
    std::pmr::vector<std::pmr::string> vec{&pool};

    std::pmr::unordered_map<int, std::pmr::string> map{&pool};
    vec.push_back(s1);
    vec.push_back(s2);
    vec.push_back(s3);
    map.insert({1, s1});
    map.insert({2, s2});
    map.insert({3, s3});

    printResult("string s1", &s1, isAddressInBuff(&s1, buff, sizeof(buff)));
    printResult("string s2", &s2, isAddressInBuff(&s2, buff, sizeof(buff)));
    printResult("string s3", &s3, isAddressInBuff(&s3, buff, sizeof(buff)));
    printResult("string s4", &s4, isAddressInBuff(&s4, buff, sizeof(buff)));
    printResult("string s5", &s5, isAddressInBuff(&s5, buff, sizeof(buff)));
    printResult("string s6", &s6, isAddressInBuff(&s6, buff, sizeof(buff)));

    printResult("s1.c_str()", (void *) s1.c_str(), isAddressInBuff((void *) s1.c_str(), buff, sizeof(buff)));
    printResult("s2.c_str()", (void *) s2.c_str(), isAddressInBuff((void *) s2.c_str(), buff, sizeof(buff)));
    printResult("s3.c_str()", (void *) s3.c_str(), isAddressInBuff((void *) s3.c_str(), buff, sizeof(buff)));
    printResult("s4.c_str()", (void *) s4.c_str(), isAddressInBuff((void *) s4.c_str(), buff, sizeof(buff)));
    printResult("s5.c_str()", (void *) s5.c_str(), isAddressInBuff((void *) s5.c_str(), buff, sizeof(buff)));
    printResult("s6.c_str()", (void *) s6.c_str(), isAddressInBuff((void *) s6.c_str(), buff, sizeof(buff)));

    printResult("vector vec", &vec, isAddressInBuff(&vec, buff, sizeof(buff)));
    printResult("unordered_map map", &map, isAddressInBuff(&map, buff, sizeof(buff)));

    printResult("vec[0]", (void *) vec[0].c_str(), isAddressInBuff((void *) vec[0].c_str(), buff, sizeof(buff)));
    printResult("vec[1]", (void *) vec[1].c_str(), isAddressInBuff((void *) vec[1].c_str(), buff, sizeof(buff)));
    printResult("vec[2]", (void *) vec[2].c_str(), isAddressInBuff((void *) vec[2].c_str(), buff, sizeof(buff)));
    printResult("map[0]", (void *) map[0].c_str(), isAddressInBuff((void *) map[0].c_str(), buff, sizeof(buff)));
    printResult("map[1]", (void *) map[1].c_str(), isAddressInBuff((void *) map[1].c_str(), buff, sizeof(buff)));
    printResult("map[2]", (void *) map[2].c_str(), isAddressInBuff((void *) map[2].c_str(), buff, sizeof(buff)));
}

bool isAddressInBuff(void *addr, void *start, std::size_t size) {
    auto start_ptr = reinterpret_cast<const std::uint8_t *>(start);
    auto addr_ptr = reinterpret_cast<const std::uint8_t *>(addr);
    return (addr_ptr >= start_ptr) && (addr_ptr < (start_ptr + size));
}

void printResult(const std::pmr::string &name, void *addr, bool isInBuff) {
    std::cout << name << ": " << ((isInBuff) ? "yes" : "no") << " - " << addr << std::endl;
}

输出:
string s1: no - 0x7ffdc4136540
string s2: no - 0x7ffdc4136570
string s3: no - 0x7ffdc41365a0
string s4: no - 0x55e8e9429eb0
string s5: no - 0x55e8e9429ee0
string s6: no - 0x55e8e9429f10
s1.c_str(): no - 0x7ffdc4136558
s2.c_str(): no - 0x7ffdc4136588
s3.c_str(): no - 0x7ffdc41365b8
s4.c_str(): no - 0x55e8e9429ec8
s5.c_str(): no - 0x55e8e9429ef8
s6.c_str(): no - 0x55e8e9429f28
vector vec: no - 0x7ffdc41364a0
unordered_map map: no - 0x7ffdc4136500
vec[0]: yes - 0x7ffdc4136690
vec[1]: yes - 0x7ffdc41366b8
vec[2]: yes - 0x7ffdc41366e0
map[0]: yes - 0x7ffdc4136850
map[1]: yes - 0x7ffdc4136740
map[2]: yes - 0x7ffdc41367e0




例子3 如何在内存池中分配一个,开发者自己写的class的实例?

#include <iostream>
#include <string>
#include <vector>
#include <memory_resource>

void check(void *addr);

class MyClass {
public:
    explicit MyClass(int value) : m_value(value) {
        std::cout << "构造函数" << std::endl;
    }

    ~MyClass() {
        std::cout << "析构函数" << std::endl;
    }

    void printValue() const {
        std::cout << "[value] " << m_value << std::endl;
    }

private:
    int m_value;
};

unsigned char buff[1024]{};

int main() {
    // 1 创建池子
    std::pmr::monotonic_buffer_resource pool(buff, 1024, std::pmr::null_memory_resource());

    // 2 创建一个pmr分配器
    std::pmr::polymorphic_allocator<MyClass> alloc(&pool);

    // 3 使用分配器的allocate()函数来分配内存
    // 两步可以缩写成一步,MyClass *myClass = std::pmr::polymorphic_allocator<MyClass>(&pool).allocate(1);
    MyClass *myClass = alloc.allocate(1);

    // 4 在内存池中构造MyClass对象
    // allocate()执行之后,并没有自动调用构造函数,需要显式手动调用
    std::pmr::polymorphic_allocator<MyClass>(&pool).construct(myClass, 42);

    // 5 调用对象的成员函数
    myClass->printValue();

    // 检查
    check(myClass);

    // 6 析构实例
    std::pmr::polymorphic_allocator<MyClass>(&pool).destroy(myClass);

    // 7 在内存池中销毁MyClass实例,回收空间
    std::pmr::polymorphic_allocator<MyClass>(&pool).deallocate(myClass, 1);

    return 0;
}

void check(void *addr) {
    // 判断实例地址是否在buff内存空间中。
    if (addr >= (void *) buff && addr < (void *) (buff + sizeof(buff))) {
        std::cout << "实例在池子中:yes" << std::endl;
    } else {
        std::cout << "实例在池子中:no" << std::endl;
    }
}

输出:
构造函数
[value] 42
实例在池子中:yes
析构函数




例子4 在内存池中定义string/vector/map实例

#include <iostream>
#include <string>
#include <vector>
#include <memory_resource>
#include <map>

void check(void *addr);

unsigned char buff[1024]{};

int main() {
    // string
    std::pmr::monotonic_buffer_resource pool(buff, 1024, std::pmr::null_memory_resource());
    std::pmr::string *str = std::pmr::polymorphic_allocator<std::pmr::string>(&pool).allocate(1);
    std::pmr::polymorphic_allocator<std::pmr::string>(&pool).construct(str, "hello_world");
    check(str);
    check((void *) (str->c_str()));

    // vector<string>
    using vec_t = std::pmr::vector<std::pmr::string>;
    vec_t *vec = std::pmr::polymorphic_allocator<vec_t>(&pool).allocate(1);
    std::pmr::polymorphic_allocator<vec_t>(&pool).construct(vec);
    vec->emplace_back("aaaa");
    vec->emplace_back("bbbb");
    check(vec);
    check((void *) vec);
    check(&((*vec)[0]));             // 第一个elem
    check(&((*vec)[1]));             // 第二个elem
    check((void *) (*vec)[0].c_str()); // 第一个elem
    check((void *) (*vec)[1].c_str()); // 第二个elem

    // map<int,string>
    using map_t = std::pmr::map<int, std::pmr::string>;
    map_t *map = std::pmr::polymorphic_allocator<map_t>(&pool).allocate(1);
    std::pmr::polymorphic_allocator<map_t>(&pool).construct(map);
    map->insert({1, std::pmr::string("cccc")});
    map->insert({2, std::pmr::string("dddd")});
    check(&(*map)[1]);
    check((void *) (*map)[1].c_str());
    return 0;
}

void check(void *addr) {
    // 判断实例地址是否在buff内存空间中。
    if (addr >= (void *) buff && addr < (void *) (buff + sizeof(buff))) {
        std::cout << "实例在池子中:yes" << std::endl;
    } else {
        std::cout << "实例在池子中:no" << std::endl;
    }
}

输出:
实例在池子中:yes
实例在池子中:yes
实例在池子中:yes
实例在池子中:yes
实例在池子中:yes
实例在池子中:yes
实例在池子中:yes
实例在池子中:yes
实例在池子中:yes
实例在池子中:yes




例子5 嵌套内存资源管理器

#include <iostream>
#include <vector>
#include <memory_resource>

void check(void *addr);

unsigned char buff[128]{};

int main() {
    std::pmr::monotonic_buffer_resource monotonic_pool(buff, sizeof(buff), std::pmr::null_memory_resource());
    std::pmr::unsynchronized_pool_resource pool(&monotonic_pool);
    std::pmr::vector<int> vec(&pool);
    for (int k = 0; k < 10000; k++) {
        vec.emplace_back(k);
    }
    return 0;
}

输出

可以看到, 超出空间后, 没有分配堆内存, 直接报错了, 这就是我想要的效果.
image



posted @ 2023-03-06 19:43  qwertzxc  阅读(603)  评论(0)    收藏  举报