zoukankan      html  css  js  c++  java
  • 智能指针和动态内存

            静态内存用来保存局部的static对象和类static数据成员,以及定义在任何函数之外的变量。除了静态内存和栈内存,每个程序还有一个内存池,这部分内存被称作自由空间或堆,用来存储动态分配的对象。动态内存的管理通过new和delete运算符实现。新的标准定义了两种智能指针类型来管理动态对象,shared_ptr允许多个指针指向同一个内存对象,unique_ptr则独占所指向的内存对象。

     1 shared_ptr<string> p1;//定义智能指针
     2 string s="Hello World!";
     3 p1=make_shared<string>();//用make_shared赋值
     4 if(p1&&p1->empty())//p1不为空,检查是否指向一个空字符串
     5        *p1=s;
     6 shared_ptr<int> p2=make_shared<int>(42);//make_shared<T>(args)
     7 shared_ptr<int> p3(p2);//p3是p2的拷贝
     8 p2.swap(p3);
     9 auto p4 = make_shared<vector<string>>();//P4指向一个空的vector<string>
    10 auto p5(p4);//p4,p5志向相同的对象。此对象有两个引用者
    11 //每个shared_ptr都有一个关联的计数器,称为引用计数,无论何时拷贝一个shared_ptr计数器都会//递增,一旦一个shared_ptr被赋予新值或离开其作用域时计数器递减,当计数器值为0自动释放管理的对象
    12 auto r=make_shared<int> (43);
    13 r=p2;//给r赋值,递增p2的引用计数,递减r的引用计数,share_ptr自动销毁对象是通过析构函数实现

    shared_ptr和unique_ptr都支持的操作
    shared_ptr<T> sp 空智能指针,可以指向类型为T的对象
    unique_ptr<T> up

    p 可以将其作为一个条件判断,若指向一个对象返回Ture
    *p 解引用,获得它指向对象内容
    p->mem等价于(*p).mem
    swap(p,q) 交换俩个指针
    p.swap(q)


    make_shared_ptr<T> (args),返回一个shared_ptr ,指向一个类型为T的对象,使用args参数初始化
    shared_ptr<T> p(q) 拷贝,增加q中的计数器
    p=q 递减p的引用计数,递增q的引用计数

     1 //test返回一个shared_ptr,可以//
     2 //确保它分配的对象在恰当的时刻被释放//
     3 shared_ptr<int> test(int a){
     4     return make_shared<int>(a);
     5 }
     6 //由于q是use_test的局部变量,函数结束是会被销毁,这个过程将递
     7 //减引用计数并检查是否为0.而q是唯一引用test返回的内存的对象,
     8 //q被销毁,q指向的这个对象也被释放
     9 void use_test(int b){
    10     shared_ptr<int> q=test(b);
    11    // return q;//这种情况下,函数返回一个q的拷贝,增加shared_ptr管理的对象的引用计数,
    12     //它所指向对象不会被释放
    13 }

     通过只能指针实现数据共享的一个例子

    //使用智能指针实现一个类,完成这样的功能:像vector一样可以
    //保存一组数据,当希望不同Blob对像的不同拷贝之间共享相同的元素。
    class StrBlob{
    public:
        typedef std::vector<std::string>::size_type size_type;//定义一个数据大小数据类型
        StrBlob();//
        StrBlob(std::initializer_list<std::string> il);//构造函数可以接受一个初始化器的花括号列表。
        size_type size() const {return data->size();}//返回数据长度
        bool empty() const {return data->empty();}//常量成员函数,相当于将this指针声明为const StrBlob *const this
        void push_back(const std::string &t) {data->push_back(t);}//添加元素
        void pop_back();//删除元素
        std::string& front();//
        std::string& back();
    private:
        std::shared_ptr<std::vector<string>> data;
        void check(size_type i,const std::string &msg) const;//data[i]不合法将抛出异常
    };
    void StrBlob::check(size_type i,const string &msg) const{
        if(i>=data->size())
            throw out_of_range(msg);
    }
    string& StrBlob::front()
    {
        check(0,"front on empty StrBlob");
        return data->front();
    }
    string& StrBlob::back()
    {
        check(0,"front on empty StrBlob");
        return data->back();
    }
    void StrBlob::pop_back()
    {
        check(0,"front on empty StrBlob");
        return data->pop_back();
    }

     直接管理内存:使用new来动态分配内存和初始化对象

     1     int *pii=new int(1024);
     2     string *pss=new string(10,'9');
     3     vector<int> *pv=new vector<int>{0,1,2,3,4,5,6,7,8,9};
     4     auto pa = new auto("obj");//编译器自己推断指针类型
     5     //auto pa1 = new auto("obj",1);//错误括号中只能有一个初始化器
     6     const int *pci = new const int(1024);//动态分配的const对象必须初始化
     7     const string *pcs = new const string("1024");
     8     delete pi;//释放内存,释放一块非new定义的内存或将相同指针释放多次行为是未定义的
     1 int * test1(int a){
     2     return new int(a);
     3 }
     4 void use_test1(int b){
     5     int * q=test1(b);
     6    //使用者必须记得释放次内存
     7     delete q;
     8 }

    编写函数,返回一个动态分配的int 的vector。将此vector传递给另一个函数,这个函数读取标准输入将读入的值保存在vector元素中,再将vector传递给另一个函数打印读入的值,记得在恰当时候释放内存

     1 vector<int> *newtest(){
     2     return new vector<int>;
     3 }
     4 void print_data(vector<int> *data){
     5     int i=0;
     6     while(i<data->size()){
     7         printf("%d ",(*data)[i]);
     8          i++;
     9     }
    10 }
    11 
    12 void input_data(){
    13     vector<int> *t=newtest();
    14     int a;
    15     while(cin>>a){
    16         t->push_back(a);
    17     }
    18     print_data(t);
    19     delete t;
    20 }

    shared_ptr和new结合使用:

     1     shared_ptr<int> pn(new int(42));//可以用一个new返回的指针来初始化智能指针
     2     //shared_ptr<int> pn1=new int(42);//错误,必须使用直接初始化
     3     unique_ptr<int> pu1(new int(42));//unique_ptr拥有它所指向的对像,不支持普通的拷贝和赋值
     4    // unique_ptr<int> pu2(p1);//错误
     5     unique_ptr<int> pu2(pu1.release());//所有权从pu1传给pu2
     6     unique_ptr<int> pu3;
     7     pu3.reset(pu2.release());//pu3从新执行新的指针,释放之前的对象
     8     auto p=make_shared<int> (42);
     9     weak_ptr<int> wp(p);//wp若共享p,不改变p的引用计数
    10     if(shared_ptr<int> np=wp.lock()){}//通过lock()函数来检查内存对象是否已将被释放
    11 int *x(new int(1024))
    12    process(x);错误,不能将int*转换成shared_ptr<int>
    13   process(shared_ptr<int>(x));正确,但内存会被释放
    void proecess(shared_ptr<int> ptr){}

    定义和改变shared_ptr的其它方法:
    shared_ptr<T> p(q) p管理内置类型q所指向的对象,q必须指向new分配的内存
    shared_ptr<T> p(u) p从unique_ptr<T> u那里接管了对象的管理权,将U置空

    unique_ptr操作:
    unique_ptr<T> u1 空指针
    u.release() 放弃对指针的控制权,将u置空
    u.reset() 释放u所指向的对象


    weak_ptr操作:
    weak_ptr<T> w 空指针,可一指向类型为T的对象
    weak_ptr<T> w(sp) 与shared_ptr sp指向相同的对象
    w.reset() 释放所指向的对象
    w.use_count()与w共享的shared_ptr数量
    w.expired() 若w.use_count为0返回true否则返回False
    w.lock() 若w.expierd()为true,返回一个空的shared_ptr否则返回一个指向w的对象的shared_ptr

    /*shared_ptr<int> clone(int p){
        return new int(p);//错误,不能隐式转换
    }*/
    shared_ptr<int> clone1(int p){
        return shared_ptr<int>(new int(p));//正确,将shared_ptr绑定到返回的指针
    }
    //shared_ptr<int> p(new int(42));//这样传值会使shared_ptr维护的内存不是放
    //int *p(new int(32));//这样将p绑定到智能指针会释放内存,但之后不能用内置指针访问
    void process(shared_ptr<int> ptr){
    
    }

    动态数组:

     1     int *pia = new int[10];//分配一个对象数组,方括号内必须是整形但不必是常量
     2     typedef int arrT[32];//表示数组类型的类型别名
     3     int *pia1=new arrT;//不需要加方括号
     4     int *pia3 = new int[10]();//动态分配数组初始化,在其后添加一堆括号
     5     int *pia4 = new int[10]{0,1,2,3,4,5,6,7,8,9};
     6     int *pia5 = new int[0];//动态分配一个空数组是合法的
     7     *pia5;
     8     delete [] pia;//释放动态分配的数组
     9     unique_ptr<int[]> up(new int[10]);//智能指针管理一个动态分配的数组。
    10     up.release();//自动用delete销毁*/

    allocator类:

     1 //allocator类
     2     int n=10;//
     3     string *const px=new string[n];
     4     string s1;
     5     string *qx=px;
     6     while(cin>>s1&&qx!=px+n){
     7         *qx++=s1;
     8         cout<<s1<<endl;
     9     }
    10     const size_t size=qx-px;
    11     delete [] px;
    12 
    13     allocator<string> alloc;//可以分配string的allocator对象
    14     auto const pa=alloc.allocate(n);//分配n个未初始化的string 对象
    15     alloc.deallocate(pa,n);//释放从pa地址开始的3个对象
    16     auto qa = pa;
    17    // alloc.construct(qa++);
    18     alloc.construct(qa++,10,'c');

    使用标准库的文本查询程序,智能指针的应用:

     1 //在设计一个类时应该先编写一个函数使用这个类
     2 void runQuery(ifstream &infile){
     3     TextQuery tq(infile);
     4     while(true){
     5         cout<<"enter word to look for,or q to quit:";
     6         string s;
     7         if(!(cin>>s)||s=="q") break;
     8         print(cout,tq.query(s)) <<endl;
     9     }
    10 
    11 }
    string make_plural (size_t ctr , const string &word ,
    const string &ending)
    {
        return ( ctr == 1 ) ? word : word + ending;
    }
    class QueryResult;
    
    class TextQuery{//TextQuery类
    public:
        using line_no=std::vector<string>::size_type;//定义一个别名
        TextQuery(std::ifstream&);//构造函数。读取文件
        QueryResult query(const std::string&) const;//查找字符串
    private:
        shared_ptr<vector<string>> file;//保存文件,每一行存到vector中
        std::map<string,shared_ptr<set<line_no>>> wm;//将单词与所在行号绑定
    
    };
    
    TextQuery::TextQuery(ifstream &is):file(new vector<string >){//构造函数,读取文件
        string text;//
        while(getline(is,text)){//一行行读
            file->push_back(text);
            int n=file->size()-1;//得到行索引
            istringstream line(text);//
            string word;
            while(line>>word){//每一行的每个单词
                auto &lines=wm[word];//map关联容器相关操作,这一句相当于在wm中插入word关键词,
                //其对应的值返回给lines也就是一个shared_ptr<set<line_no>>
                if(!lines)
                    lines.reset(new set<line_no>);//第一次这个智能指针为空,分配一个对象
                lines->insert(n);//插入单词所在行号
    
            }
        }
    }
    //查询结果类
    class QueryResult{
        friend ostream& print(ostream &,const QueryResult &);//打印查找结果
    public:
        using line_no=std::vector<string>::size_type;
        QueryResult(string s,shared_ptr<set<line_no>> p,//单词和行号
                    shared_ptr<vector<string>> f):sought(s),lines(p),file(f){}//行信息
    private:
        string sought;
        shared_ptr <set<line_no>> lines;
        shared_ptr <vector<string>> file;
    };
    ostream& print(ostream &os,const QueryResult &qr){
        os<<qr.sought<<"occurs"<<qr.lines->size()<<" "<<make_plural(qr.lines->size(),"time","s")<<endl;
        for(auto num:*qr.lines)
            os<<"	(line"<<num+1<<")"<<*(qr.file->begin()+num)<<endl;
        return os;
    }
    QueryResult TextQuery::query(const string &sought)const{
        static shared_ptr<set<line_no>> nodata(new set<line_no>);
        auto loc=wm.find(sought);
        if(loc==wm.end()){
            return QueryResult(sought,nodata,file);
        }
        else
            return  QueryResult(sought,loc->second,file);
    }
    
    
  • 相关阅读:
    简单好用的日历排期控件
    Ext.js create store
    Ext.js页面添加元素
    Ext.js Tree
    前端设计的七大法则
    如何写软件开发相关文档,它包含哪些种类和内容
    行内文字末尾下降
    正则表达式
    滚动加载数据
    location.hash来保持页面状态
  • 原文地址:https://www.cnblogs.com/bingzzzZZZ/p/8425689.html
Copyright © 2011-2022 走看看