zoukankan      html  css  js  c++  java
  • allocator class

    当分配一大块内存时,我们通常计划在这块内存上按需构造对象,这样的我们希望将内存分配和对象构造分离。但是通常的new关键字的分配的动态空间,有时候会造成一些浪费,更致命的是“如果一个类没有默认构造函数,那么这个类就不能动态分配数组了”。

    这时我们需要一个能将内存分配和对象构造分离的机制,allocator很好地帮助我们解决了这个问题。

    #include 《memory》,allocator提供了一种类型感知的内存分配方法,它分配的内存是原始的,未构造的。我们可以利用allocator提供的操作对这些内存进行操作,

    allocator<T> a                        定义了一个名为a的allocator对象,它可以为类型为T的对象分配内存
    a.allocator(n) 先定义,后分配。这分配一段原始的,未构造的,保存n个类型为T的对象;----》可以看出alllocator方法分配内存,也是先定义类型,在分配类型的数量
    a.deallocat(p,n)

    note,在此操作之前,必须堆每个在这个内存中创建的对象调用destory方法。

    释放从T*,这块内存保存了n类型为T的对象,p必须是一个先前由allocator返回的指针.且n必须是p创建时所要求大小,在调用deallocator之前,用于必须堆每个在这块内存中创建的对象调用destroy方法

    a.construct(p,args) p必须是一个T*的指针,指向一个块原始内存,args被传递给类型为T的构造函数,用在在p指向的内存中构造一个对象
    a.destroy(p) p为类型为T*的指针,此方法堆p所指对象执行析构函数
    allocator<string> alloc;
    auto const p = alloc.allcoate(n);
    
    auto q = p;
    alloc.construct(q++);//*q is a null string
    alloc.construct(q++,10,'c')// *q is cccccccccc
    alloc.construct(q++,"hi")//*q is hi
    
    在未构造对象的情况下,就使用原始内存是错误的
    例如
    cout<<*p<<endl;
    cout<<*q<<endl;//这个是非法的
    
    记住,为了使用allcocate返回的内存,我们必须用construct构造对象,使用未构造的函数,其行为是未定义的.
    党我们使用完对象后,必须堆每个构造函数调用destroy来销毁
    while(q!=p){
      alloc.destroy(--q);  
    }//我们只能堆真正构造了的元素进行destroy操作

    //一旦元素被销毁后,我们重新使用这部分内存来保存其他string,
    //也可以将内存归还给os,释放内存是通过deallocate(p,n)来完成的

    alloc.deallocate(p,n);

    拷贝/填充未初始化的算法,STL还定义了两个伴随算法,可以在未初始化内存中创建对象

    uninitialized_copy(b,e,b2) 从迭代器b和e指出的输入范围拷贝元素到迭代器b2指定的未构造的原始原始内存中,b2指向的内存必须足够大,能容纳输入序列中元素的拷贝
    uninitiated_copy(b,n,b2)  
    uninitiated_fill(b,e,t)

    在迭代器b和e指定的原始内存范围中创建对象,对象的值均为t的拷贝

    uninitiated_fill(b,n,t)  
    /*
    假定有一个例子,希望将其内容拷贝到动态内存中,我们将分配一块比vector中元素空间大一倍的动态内存,然后将原vector中的元素拷贝到前一半空间,后一半空间利用一个给定值进行填充
    */
    
    auto p = alloc.allocate(vi.size()*2);
    
    auto q = uninitiated_copy(vi.begin(),vi.end(),p);
    
    uninitiated_fill(q,vi.size(),42);

     test case

     1 #ifndef STRVEC_H_INCLUDED
     2 #define STRVEC_H_INCLUDED
     3 #include<vector>
     4 #include<string>
     5 #include<memory>
     6 #include<utility>
     7 #include<iostream>
     8 /**
     9 这里要实现的是一个STLvector的简化版,只能运用于string,由于没有template.
    10 设计思路:
    11 1,预先分配足够大的内存来保存可能需要的更多的元素,
    12 2,当StrVec添加每个元素时,成员函数chk_n_alloc()会检查是否有足够空间容纳更多的元素
    13   2.1 如果有,alloc.construct()会在下一个可用位置构造一个对象
    14   2.2 如果没有,StrVec会充分分配空间alloc_n_copy(),并且拷贝一个给定范围中的元素,然后释放掉旧的空间free()
    15         并添加新的元素
    16 
    17 
    18         ---------------------------------------------
    19         |   |   |   |   |                           |
    20         ---------------------------------------------
    21         ^                 ^                           ^
    22         elements          first_free                  cap
    23         --
    24 每个StrVec有三个指针指向其元素所使用的内存:
    25 elements,指向分配中的首元素
    26 frist_free,指向最后一个实际元素位置之后的位置
    27 cap,指向分配的内存末尾之后的位置
    28 ------------------
    29 
    30 StrVec除了指出位置的指针外,还有名为alloc的静态成员,类型为allocator<string>,它分配我们所需要的内存
    31 ---
    32 另外还有4个工具函数,
    33 alloc_n_copy(),分配内存并拷贝一个给定范围中的元素
    34 free()//销毁构造的元素病释放内存
    35 chk_n_alloc()//保证StrVec至少能容纳一个新元素的空间,如果没有空间添加新元素,chk_n_alloc会调用reallocate()来分配更多的空间按
    36 reallocate()//在内存使用完后,reallocate()重新分配元素
    37 
    38 */
    39 
    40 
    41 
    42 using namespace std;
    43 class StrVec{
    44 public:
    45     StrVec():elements(nullptr),first_free(nullptr),cap(nullptr){}///构造函数,对成员进行初始化
    46     StrVec(const StrVec&);///拷贝构造函数,从无到有
    47     StrVec &operator=(const StrVec &);///拷贝赋值运算符=,对存在的元素赋予新值
    48     ~StrVec();///析构函数
    49 
    50     void push_back(const std::string&);///拷贝元素
    51 
    52     size_t size() const {return first_free - elements;}///返回StrVec中的元素个数
    53     size_t capacity() const {return cap - elements;}///返回StrVec中实际分配的(可以容纳类型元素的)内存大小
    54     string *begin() const {return elements;}///返回首元素
    55     string *end() const {return first_free;}///返回StrVec末尾元素的后一个位置
    56 
    57 
    58 private:
    59     static allocator<string> alloc;///分配元素,注意是静态成员
    60     void chk_n_alloc(){///被添加元素的函数使用
    61         if(size() == capacity())
    62             reallocate();
    63     }///
    64     ///工具函数,被拷贝构造函数,赋值运算符和析构函数使用
    65     pair<string*,string*> alloc_n_copy(const string*,const string*);///什么时候没有空间容纳新元素呢? first_free==cap的时候.
    66     void free();///撤销元素病释放内存
    67     void reallocate();///获得更多内存并且拷贝已有元素
    68 
    69     string *elements;///指向数组首元素的指针
    70     string *first_free;///指向数组第一个空闲元素的指针
    71     string *cap;///指向数组尾后位置的指针
    72 };
    73 
    74 void show();
    75 
    76 #endif // STRVEC_H_INCLUDED

    ----

     1 #include "StrVec.h"
     2 using namespace std;
     3 
     4 allocator<string> StrVec::alloc;
     5 
     6 void show(){
     7     std::cout<<"from StrVec.cpp"<<std::endl;
     8 }
     9 
    10 void StrVec::push_back(const string& s){
    11     chk_n_alloc();///
    12 
    13     alloc.construct(first_free++,s);
    14 }
    15 
    16 
    17 ///函数返回一个pair,两个指针分别指向新空间的开始位置和拷贝的尾后位置,即data是新空间开始的位置,uninitiated_copy()返回新空间拷贝的尾后位置
    18 pair<string*, string*>
    19 StrVec::alloc_n_copy(const string *b,const string *e){
    20     auto data = alloc.allocate(e-b);
    21     return make_pair(data,uninitialized_copy(b,e,data));
    22     //return {data,unintialized_copy(b,e,data)};
    23 }
    24 
    25 void StrVec::free(){
    26     ///can not pass a nullptr to deallocate(),if elements is 0,then free() do nothing
    27     if(elements){
    28         for(auto p = first_free;p != elements;/* */){
    29             StrVec::alloc.destroy(--p);
    30         }
    31         StrVec::alloc.deallocate(elements,cap-elements);
    32     }
    33 }
    34 
    35 StrVec::StrVec(const StrVec &s){
    36     auto newdata = alloc_n_copy(s.begin(),s.end());
    37     elements = newdata.first;
    38     first_free = newdata.second;
    39     cap = newdata.second;
    40 }
    41 
    42 StrVec::~StrVec(){
    43     free();
    44 }
    45 
    46 StrVec &StrVec::operator=(const StrVec &rhs){
    47     ///
    48     auto data = alloc_n_copy(rhs.begin(),rhs.end());
    49     free();///释放掉已有元素之前,调用alloc_n_copy缓存rhs的元素,这可以处理自我赋值
    50     elements = data.first;
    51     first_free = data.second;
    52     cap = data.second;
    53 
    54     return *this;///
    55 }
    56 
    57 /**
    58 在重新分配内存的过程中移动而不是拷贝元素
    59 我们reallocate()应该做些什么呢?
    60 1,为一个新的,更大的string数组分配元素
    61 2,在内存空间的前一部分构造,保存现有元素
    62 3,销毁原来内存空间中的元素,并释放这块内存
    63 */
    64 
    65 void StrVec::reallocate(){
    66     auto newcapacity = size() ? 2*size():1;
    67 
    68     auto newdata = StrVec::alloc.allocate(newcapacity);
    69 
    70     auto dest = newdata;
    71     auto elem = elements;
    72 
    73     for(size_t i = 0;i != size();i++){
    74         StrVec::alloc.construct(dest++,std::move(*elem++));
    75     }
    76 
    77     free();
    78 
    79     elements = newdata;
    80     first_free = dest;
    81     cap = elements + newcapacity;
    82 }

    -----main.cpp

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <map>
    #include <set>
    #include <unordered_set>
    #include <unordered_map>
    #include "StrVec.h"
    using namespace std;
    
    
    
    int main()
    {
        show();
        StrVec sv;
        sv.push_back("aaa");
        sv.push_back("bbb");
    
        int length = sv.size();
        string *ptr = sv.begin();
        for(int i = 0;i<length;i++){
            cout<<*ptr++<<endl;
        }
    
        StrVec svb(sv);
        string *ptrb = svb.begin();
        for(int i = 0;i<svb.size();i++){
            cout<<*ptrb++<<endl;
        }
    
        StrVec svc = sv;
        string *ptrc = svb.begin();
        for(int i = 0;i<svc.size();i++){
            cout<<*ptrc++<<endl;
        }
        return 0;
    }

    ================

  • 相关阅读:
    并发编程三、线程可见性的底层原理
    并发编程二、线程的安全性和线程通信
    并发编程一、多线程的意义和使用
    Java进程监控
    分布式消息通信之RabbitMQ_Note
    分布式消息通信之RabbitMQ_02
    分布式消息通信之RabbitMQ_01
    分布式消息通信之RabbitMQ Tutorials
    SpringMVC重点分析
    Apache POI 4.0.1版本读取本地Excel文件并写入数据库(兼容 xls 和 xlsx)(五)
  • 原文地址:https://www.cnblogs.com/li-daphne/p/5451786.html
Copyright © 2011-2022 走看看