智能指针和动态内存

Posted bingzzzzzz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能指针和动态内存相关的知识,希望对你有一定的参考价值。

        静态内存用来保存局部的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<<"\t(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);
}

 

 

以上是关于智能指针和动态内存的主要内容,如果未能解决你的问题,请参考以下文章

C++ - 指针和“智能指针”

动态内存与智能指针

c++动态内存管理与智能指针

动态内存1(动态内存与智能指针)

Chapter12:动态内存

动态内存和智能指针