C++智能指针模板怎么应用
本篇内容介绍了“C++智能指针模板怎么应用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
智能指针模板类
void remodel(std::string & str) { std::string *ps =new std::string(str); .... if(oh_no) throw exception(); ... delete ps; return; }
如果上面这段函数出现异常,那么就会发生内存泄漏。传统指针在执行动态内存分配时具有缺陷,很容易导致内存泄漏。如果有一个指针,指针被释放的同时,它指向的内存也能被释放,那就完美了。这种指针就是智能指针。
我们只介绍3个智能指针模板类:auto_ptr
、unique_ptr
、shared_ptr
,顺便会提一下weak_ptr
。其中auto_ptr
已经被抛弃了,它是一个过时的产物,我们介绍它只为抛砖引玉。
使用智能指针
这些智能指针模板定义了类似指针的对象,我们把new
获得的地址赋给这种对象,当智能指针过期时,它的析构函数会自动释放动态内存。
必须包含头文件memory
,这个文件包含模板定义。我们使用模板实例化来创建所需指针.
类模板大概是:
template<class X> class auto_ptr { public: explicit auto_ptr(X* p) noexcept; .... }
所以我们实例化:
auto_ptr<double> pd(new double);
或者auto_ptr<string> ps(new string);
。
对于其他两种智能指针也是一样的构造语法。
//智能指针1.cpp #include<iostream> #include<string> #include<memory> class Report { private: std::string str; public: Report(const std::string &s):str(s){std::cout<<"Object created!\n";} ~Report(){std::cout<<"Object deleted";} void comment() const{std::cout<<str<<"\n";} }; int main() { { std::auto_ptr<Report> ps(new Report("using auto_ptr")); ps->comment(); } { std::unique_ptr<Report> ps(new Report("using unique_ptr")); ps->comment(); } { std::shared_ptr<Report> ps(new Report("using shared_ptr")); ps->comment(); } }
Object created!
using auto_ptr
Object deletedObject created!
using unique_ptr
Object deletedObject created!
using shared_ptr
Object deleted
注意,所有智能指针类都有一个explicit
构造函数,该构造函数将指针作为参数。所以它不会将指针转换成智能指针对象。
shared_ptr<double> pd; double *p_reg=new double; pd=p_reg;//不允许因为构造函数是`explicit`修饰的,所以不能隐式类型转换 pd=shared_ptr<double>(p_reg);//允许,使用赋值运算符 shared_ptr<double> pshared=p_reg;//不允许,因为不能隐式类型转换 shared_ptr<double> pshared(p_reg);//允许调用构造函数。
智能指针和传统指针有很多类似的语法:例如可以使用*ps
来解除引用,也可以使用ps->
来访问结构成员。
但是最重要的不同是:我们只能用能进行delete
或者delete[]
的指针来构造智能指针。
也就是说:
int a=5; int *p=&a; shared_ptr<int> ps(p);
上面这段代码就是错误的,因为p
无法使用delete
.
#include<memory> #include<iostream> int main() { using namespace std; /*{ auto_ptr<int[]> ps(new int[2]{1,2}); cout<<ps[0]<<endl; cout<<ps[1]<<endl; }*/ { unique_ptr<int[]> ps(new int[2]{1,2}); cout<<ps[0]<<endl; cout<<ps[1]<<endl; } { shared_ptr<int []> ps(new int[2]{1,2}); cout<<ps[0]<<endl; cout<<ps[1]<<endl; } }
上面代码告诉我们,我们可以使用<int []>
这样实例化模板,这是为了模拟动态数组。
关于智能指针的注意事项
auto_ptr<string> ps(new string("I reigned lonely as a cloud.")); auto_ptr<string> pd; pd=ps;
上面这段代码不会报错,但是你可能会问:pd和ps都是智能指针,如果我们把ps赋给pd;那么就说明这两个指针指向同一个string
对象,那么当这两个指针消失时,会对同一个string
对象释放两次?
我们看看我们如何避免这种问题:
进行深复制
建立所有权概念,对于特定的对象,只能有一个智能指针拥有它,这样就只有拥有它的智能指针的析构函数才会释放内存。然后赋值运算会转让所有权。
auto_ptr
和unique_ptr
都是使用这种策略,但是unique_ptr
更严格。使用引用计数,每个对象都会记录有多少个智能指针指向它,然后赋值运算时,计数加1,指针过期时计数减1。当最后一个指针过期时,才会调用
delete
,这是shared_ptr
的策略。
实际上,上述这些策略也适用于复制构造函数。
实际上,unique_ptr
就是"唯一指针",指针和被指向的对象一一对应,而shared_ptr
就是"分享指针",它允许多个指针指向同一个对象。所以说,shared_ptr
的用法更像C风格指针。
我们看上面的代码,pd=ps
后,由于string
对象的所有权交给了pd
,所以*ps
就无法使用了。
//智能指针3.cpp #include<iostream> #include<string> #include<memory> int main() { using namespace std; auto_ptr<string> films[5]= { auto_ptr<string>(new string("1")), auto_ptr<string>(new string("2")), auto_ptr<string>(new string("3")), auto_ptr<string>(new string("4")), auto_ptr<string>(new string("5")) }; auto_ptr<string> p; p=films[2]; for(int i=0;i<5;i++) { cout<<*films[i]<<endl; } cout<<*p<<endl; }
上面这段代码会出错,因为p=films[2];
使得,films[2]
的所有权转让给p
了,所以cout<<*film[2]
就会出错。但是如果使用shared_ptr
代替auto_ptr
就可以正常运行了。如果使用unique_ptr
呢?程序会在编译阶段报错,而不是在运行阶段报错,所以说unique_ptr
更加严格。
unique_ptr优于auto_ptr
首先就是上面谈过的,unique_ptr
的所有权概念比auto_ptr
要严格,所以unique_ptr
更加安全。
unique_ptr<string> ps(new string("I reigned lonely as a cloud.")); unique_ptr<string> pd; pd=ps;
上述代码会在编译阶段报错,因为出现了危险的悬挂指针ps
(即野指针,指针指向被删除的内存,如果使用野指针修改内存是会造成严重后果)。
但是有时候将一个智能指针赋给另一个并不会留下悬挂指针:
unique_ptr<string> demo(const char*s) { unique_ptr<string> temp(new string(s)); return temp; } ... unique_ptr<string>ps; ps= demo("something"); ...
demo()
函数返回一个临时变量temp
,然后临时变量temp
被赋给ps
,那么temp
就变成悬挂指针了,但是我们知道ps=demo("something")
一旦运行结束,demo()
里的所有局部变量都会消失包括temp
。所以即使temp
是野指针,我们也不会使用它。神奇的是,编译器也允许上面这种赋值。
总之,程序试图将一个unique_ptr
赋给另一个时,如果源unique_ptr
是个临时右值,编译器允许这么做;如果源unique_ptr
会存在一段世界,编译器禁止这么做。
unique_ptr<string> pu1; pu1=unique_ptr<string>(new string("yo!"));
上面这段代码也是允许的,因为unique_ptr<string>(new string("yo!"))
是一个临时右值(右值都是临时的,右值只在当前语句有效,语句结束后右值就会消失)
unique_ptr<string> ps(new string("I reigned lonely as a cloud.")); unique_ptr<string> pd; pd=std::move(ps);
上面代码是正确的,如果你想要进行将unique_ptr
左值,赋给unique_ptr
左值,那么你必须使用move()
函数,这个函数会将左值转换成右值。
以上所说,反映了一个事实:unique_ptr
比auto_ptr
安全。其实unique_ptr
还有一个优点:auto_ptr
的析构函数只能使用delete
,而unique_ptr
的析构函数可以使用delete[]
和delete
。
选择智能指针
首先明确一个事实:shared_ptr
更方便;unique_ptr
更安全。
如果程序需要适用多个指向同一个对象的指针,那么只能选择shared_ptr
;如果不需要多个指向同一个对象的指针,那么两种指针都可以使用。总之,嫌麻烦的话就全部用shared_ptr
.
#include<memory> #include<iostream> #include<vector> #include<cstdlib> #include<algorithm> std::unique_ptr<int> make_int(int n) { return std::unique_ptr<int>(new int(n)); } void show(const std::unique_ptr<int> &pi) { std::cout<<*pi<<' '; } int main() { using std::vector; using std::unique_ptr; using std::rand; int size=10; vector<unique_ptr<int>> vp(size); for(int i=0;i<size;i++) vp[i]=make_int(rand()%1000);//#1 vp.push_back(make_int(rand()%1000));//#2 std::for_each(vp.begin(),vp.end(),show);//#3 }
上面这段代码是使用unique_ptr
写的,#1.#2是没有问题的,因为函数返回值是临时右值,#3就要注意了.show()
函数使用的是引用参数,如果换成按值传递,那就会出错,因为这会导致,使用unique_ptr
左值初始化pi
,这时不允许的,记得吗?在使用unique_ptr
时,它的赋值运算符要求:只能用右值赋给左值。(实际上,它的复制构造函数也要求只接受右值)。
当unique_ptr
是右值的时候,我们可以把他赋给shared_ptr
。
shared_ptr
包含一个显式构造函数,他会把右值unique_ptr
转换成shared_ptr
:
unique_ptr<int> pup(make_int(rand()%1000));//ok shared_ptr<int> spp(pup);//不允许,构造函数不能接受`unique_ptr`的左值 shared_ptr<int> spr(make_int(rand()%1000));//ok
weak_ptr
weak_ptr
正如它名字所言:一个虚弱的指针,一个不像是指的指针。weak_ptr
是用来辅助shared_ptr
的。
为什么说weak_ptr
不像指针呢?是因为它没有重载*
和[]
运算符。
通常,我们使用shared_ptr
来初始化weak_ptr
,那么这两个指针都指向是同一块动态内存。
weak_ptr
是shared_ptr
的辅助,所以它帮忙能查看这块动态内存的信息:包括引用计数、存的信息。
#include<memory> #include<iostream> int main() { using std::shared_ptr; using std::weak_ptr; using std::cout; using std::endl; shared_ptr<int> p1(new int(255)); weak_ptr<int>wp(p1); cout<<"引用计数: "<<wp.use_count()<<endl; cout<<"存储信息: "<<*(wp.lock())<<endl; shared_ptr<int> p2=p1; cout<<"引用计数: "<<wp.use_count()<<endl; cout<<"存储信息: "<<*(wp.lock())<<endl; }
引用计数: 1
存储信息: 255
引用计数: 2
存储信息: 255
weak_ptr
的类方法中use_count()
查看指向和当前weak_ptr
指针相同的shared_ptr
指针的数量,lock()
函数返回一个和当前weak_ptr
指针指向相同的shared_ptr
指针。
“C++智能指针模板怎么应用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注蜗牛博客网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:niceseo99@gmail.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
评论