zoukankan      html  css  js  c++  java
  • C++ Primer学习笔记 原始内存分配类allocator

    为什么会有allocator类

    new将内存分配和对象构造组合到了一起,delete将对象析构和内存释放页组合到了一起。
    当申请分配一大块内存时,我们通常希望将内存分配和对象构建分离开。比如,下面将内存和对象构造组合到一起可能导致不必要的浪费:

    string *const p = new string[n]; // 申请n个string内存空间,并调用default构造函数构造n个空的string对象
    string s;
    string *q = p;
    while (cin >> s && q != p + n)
        *q++ = s;  // 对*q赋新值
    const size_t size = q - p;
    // 对数组进行读写等操作
    // ...
    
    delete[] p; // 析构数组的每个对象元素,并释放数组空间
    

    可以从上面代码看出,我们对申请的n个string内存空间的每个元素,都构建了2次string对象:一次是使用new申请内存的时候,另外一次是遍历数组对*q赋值的时候。

    如何解决这个问题?避免2次对象的构建?
    这就需要引入allocator类。

    allocator类

    头文件
    提供分配原始的、未构造的内存的方法,可以将内存分配和对象构造分离开来。

    allocator是一个class template,我们这样定义一个allocator对象并申请分配n个未初始化的string:

    allocator<string> alloc; // 定义一个可以分配string的allocator对象
    auto const p = alloc.allocate(n); // 分配n个原始string内存空间
    

    allocator常用操作:

    allocator<T> a;
    
    a.allocate(n); // 分配一段原始的、未构造的内存空间,该空间为n个原始的T类型对象大小,并返回首地址
    a.deallocate(p, n); // 释放allocate分配的空间,首地址p,空间为n个T类型对象大小
    
    a.construct(p, args); // 在p所指向的内存构造一个T对象,p必须是T*类型指针,指向一块原始内存,args是构造T类型对象所需要参数
    a.destroy(p); // 析构p所指向内存空间上的T对象,p必须是T*类型指针
    

    allocator构造对象

    allocator分配的内存是原始的、未构造的内存空间(称为raw memory),要在其基础上构造对象,就要使用constructor方法构造对象,用destroy方法析构对象。

    // 定义allocator对象
    allocator<string> alloc; // 定义一个可以分配string的allocator对象
    auto const p = alloc.allocate(n); // 分配n个原始string内存空间
    
    // 利用allocator对象,在raw memory上构造string对象
    auto q = p;
    alloc.construt(q++); // *q为空字符串,并且q指向下一个待构造string对象首地址
    alloc.construct(q++, 10, 'c'); // *q为cccccccccc
    alloc.construct(q++, "hi"); // *q为hi
    
    // 析构构造的对象
    // 析构之后对象便销毁了,内存重新变成raw memory
    while (q != p)
        alloc.destory(--q);
    
    // 释放内存空间,归还给系统
    // 释放内存后不可再访问
    alloc.deallocate(p, n);
    

    注意:deallocate释放内存前,最好先调用destory析构对象,因为构造的对象可能包含其他资源依赖于对象的析构函数释放。

    拷贝和填充raw memory的算法

    除了可以利用allocator的constructor在raw memory上构造对象,还可以用标准库的2个伴随算法(uninitialized_copy和uninitialized_fill)来对raw memory进行填充。

    #include <memory>
    
    uninitialized_copy(b, e, b2); // 从指定迭代器范围[b, e)拷贝所有元素到b2指向的raw memory中。b2指向的memory必须足够大,以容纳输入序列中元素拷贝。返回copy后的目的位置迭代器
    
    uninitialized_copy_n(b, n, b2);  // 从指定迭代器范围[b, b+n)拷贝n个元素到b2指向的raw memory中。返回copy后的目的位置迭代器
    
    unintialized_filled(b, e, t); // 在指定迭代器范围[b, e)中创建对象,所有对象值均为t的拷贝。返回填充后的目的位置迭代器
    
    unintialized_filled_n(b, n, t); // 在指定迭代器范围[b, b+n)中创建n个对象,所有对象值均为t的拷贝。返回填充后的目的位置迭代器
    

    例如,

    vector<int> vi = {1,2,3,4,5,6,7,8,9};
    allocator<int> alloc;
    auto p = alloc.allocate(vi.size() * 2); // 申请vi 2倍大尺寸的raw memory
    auto q = unintialized_copy(vi.begin(), vi.end(), p); // 拷贝vi所有元素到p开头的内存块中
    unintialized_fill_n(q, vi.size(), 42); // 将p的剩余空间全部填充位42
    

    我们用allocator重新开头new写的那段程序:

    /* 原始程序 */
    int n = 10;
    string *const p = new string[n]; // 申请n个string内存空间,并调用default构造函数构造n个空的string对象
    string s;
    string *q = p;
    while (cin >> s && q != p + n)
        *q++ = s;  // 对*q赋新值
    const size_t size = q - p;
    // 对数组进行读写等操作
    // ...
    
    delete[] p; // 析构数组的每个对象元素,并释放数组空间
    
    /* allocator改写程序 */
    int n = 10;
    allocator<string> alloc;
    auto p = alloc.allocate(n);
    
    string s;
    auto q = p;
    while (cin >> s && q != p + n)
    {
        alloc.construct(q++, s);
    }
    
    alloc.deallocate(p, n);
    
  • 相关阅读:
    C#里List.Sort的用法
    解决在IE中返回JSON格式的数据时提示下载的问题
    关于json语句的相关用法
    vector数组的相关知识
    编程珠矶第一章阅读笔记
    第三周学习进度博客
    echarts以地图形式显示中国疫情情况实现点击省份下钻
    课堂作业,疫情数据统计(柱形图的生成)
    过滤器filter学习进度一
    android开发对应高德地图定位服务进度一
  • 原文地址:https://www.cnblogs.com/fortunely/p/15645876.html
Copyright © 2011-2022 走看看