智能指针和动态内存
静态内存用来保存局部的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); }