C++11智能指针 share_ptr,unique_ptr,weak_ptr用法
0x01 智能指针简介
所谓智能指针(smart pointer)就是智能/自动化的管理指针所指向的动态资源的释放。它是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数(reference count)。
使用智能指针需要头文件 #include<memory> 。
C++11从boost库中引入了unique_ptr, shared_ptr, weak_ptr,并舍弃了c98的auto_ptr。
0x02 auto_ptr
C++11从boost库中引入了unique_ptr, shared_ptr, weak_ptr,并舍弃了c98的auto_ptr。
C++标准程序库描述:“auto_ptr是一种智能指针,帮助程序员防止'被异常抛出时发生资源泄露'”。它在对象析构的时候自动释放资源,并模仿了原始指针的操作,重载了operator*和operator->,允许程序员向使用原始指针一样使用auto_ptr(但是没有重载所有的指针算术运算),并减少程序员显示的处理异常发生时代码的复杂度与失误。
该指针的定义形式:auto_ptr<type> ptr(new type()); 其中 type 是指针指向的类型。
例如:auto_ptr<int> ptr(new int(4));
当然也可以先定义,后赋值:auto_ptr<int> ptr;ptr = auto_ptr<int>(new int (4));
auto_ptr的作用:作用1:保证一个对象在某个时间只能被一个该种类型的智能指针所指向,就是通常所说的对象所有权。
作用2:对指向的对象自动释放的作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include "stdafx.h" #include <Windows.h> #include <memory> #include <iostream> using namespace std; struct MyStruct { MyStruct() { cout << "MyStruct()\n" ; } ~MyStruct() { cout << "~MyStruct()\n" ; } int i; int j; }; int main() { auto_ptr<MyStruct> ptr( new MyStruct); ptr->i = 1; ptr->j = 2; (*ptr).i = 3; (*ptr).j = 4; return 0; } |
auto_ptr是严格的拥有权类智能指针,使用时需要注意以下几点:
- auto_ptr之间不能共享拥有权
- auto_ptr对象通过赋值或构造转移拥有权,一旦拥有权转移,此auto_ptr所拥有的将是一个原始指针
- auto_ptr不适用于array
- auto_ptr不满足STL对容器元素的要求,因此不适用于STL容器。因为在拷贝和赋值之后,新的auto_ptr和旧的auto_ptr对象并不相等。
- 如果要阻止拥有权的转移,则应该在停止转移之前,将auto_ptr声明为const
- 不要使用auto_ptr的引用作为实参:因为你不知道拥有权到底有没有转移。如果你不需要转移拥有权,请使用const auto_ptr<class> &
0x03 shared_ptr
shared_ptr采用引用计数的方式管理所指向的对象。当有一个新的shared_ptr指向同一个对象时(复制shared_ptr等),引用计数加1。当shared_ptr离开作用域时,引用计数减1。当引用计数为0时,释放所管理的内存。
这样做的好处在于减轻了程序员手动释放内存的负担。之前,为了处理程序中的异常情况,往往需要将指针手动封装到类中,通过析构函数来释放动态分配的内存;现在这一过程就可以交由shared_ptr完成了。
一般使用make_shared来获得shared_ptr。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | void Shared_PtrTest() { //shared_ptr Test //Test One cout << "test shared_ptr base usage:" << endl; shared_ptr<string> v1 = make_shared<string>( "" ); if (v1 && v1->empty()) *v1 = "Chronic" ; auto v2 = make_shared<string>( "LSH" ); cout << *v1 << ' ' << *v2 << endl; cout << "test shared_ptr use_count:" << endl; cout << "v1 ReferenceCount:" << v1.use_count() << "\tv2 ReferenceCount:" << v2.use_count() << endl; auto v3 = v2; cout << "v1 ReferenceCount:" << v1.use_count() << "\tv2 ReferenceCount:" << v2.use_count() << "\tv3 ReferenceCount:" << v3.use_count() << endl; v2 = v1; cout << "v1 ReferenceCount:" << v1.use_count() << "\tv2 ReferenceCount:" << v2.use_count() << "\tv3 ReferenceCount:" << v3.use_count() << endl; //Test Two //使用一个new表达式返回的指针进行初始化。 cout << "test shared_ptr and new:" << endl; shared_ptr< int > p4( new int (1024)); //shared_ptr<int> p5 = new int(1024); // 错误 cout << *p4 << endl; //Test Three //不可混用new和shared_ptr! cout << "不可混用new和shared_ptr!" << endl; shared_ptr< int > p5( new int (1024)); process(p5); int v5 = *p5; cout << "v5: " << v5 << endl; int *p6 = new int (1024); //shared_ptr<int> p6(new int(1024)); 正确做法 process(shared_ptr< int >(p6)); int v6 = *p6; cout << "v6: " << v6 << endl; /* 输出结果: 不可混用new和shared_ptr! in process use_count:2 v5: 1024 in process use_count:1 v6: -572662307 */ //Test Four //shared_ptr可以通过reset方法重置指向另一个对象,此时原对象的引用计数减一。 //shared_ptr<string> v1 = make_shared<string>("LSH"); cout << "test shared_ptr reset:" << endl; cout << "p1 cnt:" << v1.use_count() << endl; v1.reset( new string( "LSH Reset" )); cout << "p1 cnt:" << v1.use_count() << endl; } |
0x04 unique_ptr
unique_ptr对于所指向的对象,如其名所示,是独占的。所以,不可以对unique_ptr进行拷贝、赋值等操作,但是可以通过release函数在unique_ptr之间转移控制权。
对于拷贝的限制,有两个特殊情况,即unique_ptr可以作为函数的返回值和参数使用,这时虽然也有隐含的拷贝存在,但是并非不可行的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | void Unique_PtrTest() { //Test One //unique_ptr对于所指向的对象,正如其名字所示,是 独占 的。 //所以,不可以对unique_ptr进行拷贝、赋值等操作,但是可以通过release函数在unique_ptr之间转移控制权。 cout << "test unique_ptr base usage:" << endl; unique_ptr< int > up1( new int (1024)); cout << "up1: " << *up1 << endl; unique_ptr< int > up2(up1.release()); cout << "up2: " << *up2 << endl; //unique_ptr<int> up3(up1); // 错误,不可进行拷贝操作 //up2 = up1; // 错误,不可进行拷贝操作 unique_ptr< int > up4( new int (1025)); up4.reset(up2.release()); cout << "up4: " << *up4 << endl; //Test Two //上述对于拷贝的限制,有两个特殊情况, //即unique_ptr可以作为函数的返回值和参数使用,这时虽然也有隐含的拷贝存在,但是并非不可行的。 cout << "test unique_ptr parameter and return value:" << endl; auto up5 = CloneFunction(1024); cout << "up5: " << *up5 << endl; process_unique_ptr(move(up5)); //cout<<"up5 after process: "<<*up5<<endl; //错误segmentfault } |
0x05 weak_ptr
weak_ptr一般和shared_ptr配合使用。它可以指向shared_ptr所指向的对象,但是却不增加对象的引用计数。这样就有可能出现weak_ptr所指向的对象实际上已经被释放了的情况。因此,weak_ptr有一个lock函数,尝试取回一个指向对象的shared_ptr。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void Weak_PtrTest() { //weak_ptr一般和shared_ptr配合使用。 //它可以指向shared_ptr所指向的对象,但是却不增加对象的引用计数。 //这样就有可能出现weak_ptr所指向的对象实际上已经被释放了的情况。因此,weak_ptr有一个lock函数,尝试取回一个指向对象的shared_ptr。 cout << "test weak_ptr basic usage:" << endl; auto p10 = make_shared< int >(1024); weak_ptr< int > wp1(p10); assert (p10.use_count() == 1); cout << "p10 use_count: " << p10.use_count() << endl; //p10.reset(new int(1025)); // this will cause wp1.lock() return a false obj shared_ptr< int > p11 = wp1.lock(); assert (p11.use_count() == 2); if (p11) cout << "wp1: " << *p11 << " use count: " << p11.use_count() << endl; } |
0x06 小结
shared_ptr采用引用计数的方式管理所指向的对象。shared_ptr可以使用一个new表达式返回的指针进行初始化;但是,不能将一个new表达式返回的指针赋值给shared_ptr。
一旦将一个new表达式返回的指针交由shared_ptr管理之后,就不要再通过普通指针访问这块内存;shared_ptr可以通过reset方法重置指向另一个对象,此时原对象的引用计数减一。
unique_ptr对于所指向的对象,是独占的;不可以对unique_ptr进行拷贝、赋值等操作,但是可以通过release函数在unique_ptr之间转移控制权;unique_ptr可以作为函数的返回值和参数使用。
weak_ptr一般和shared_ptr配合使用;它可以指向shared_ptr所指向的对象,但是却不增加对象的引用计数;weak_ptr有一个lock函数,尝试取回一个指向对象的shared_ptr。
完整源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | // 智能指针.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <Windows.h> #include <string> #include <iostream> #include<memory> #include<assert.h> using namespace std; void Shared_PtrTest(); void Unique_PtrTest(); void Weak_PtrTest(); int main() { //Shared_PtrTest(); //Unique_PtrTest(); Weak_PtrTest(); } void process(shared_ptr< int > ptr) { cout << "in process use_count:" << ptr.use_count() << endl; } void Shared_PtrTest() { //shared_ptr Test //Test One cout << "test shared_ptr base usage:" << endl; shared_ptr<string> v1 = make_shared<string>( "" ); if (v1 && v1->empty()) *v1 = "Chronic" ; auto v2 = make_shared<string>( "LSH" ); cout << *v1 << ' ' << *v2 << endl; cout << "test shared_ptr use_count:" << endl; cout << "v1 ReferenceCount:" << v1.use_count() << "\tv2 ReferenceCount:" << v2.use_count() << endl; auto v3 = v2; cout << "v1 ReferenceCount:" << v1.use_count() << "\tv2 ReferenceCount:" << v2.use_count() << "\tv3 ReferenceCount:" << v3.use_count() << endl; v2 = v1; cout << "v1 ReferenceCount:" << v1.use_count() << "\tv2 ReferenceCount:" << v2.use_count() << "\tv3 ReferenceCount:" << v3.use_count() << endl; //Test Two //使用一个new表达式返回的指针进行初始化。 cout << "test shared_ptr and new:" << endl; shared_ptr< int > p4( new int (1024)); //shared_ptr<int> p5 = new int(1024); // 错误 cout << *p4 << endl; //Test Three //不可混用new和shared_ptr! cout << "不可混用new和shared_ptr!" << endl; shared_ptr< int > p5( new int (1024)); process(p5); int v5 = *p5; cout << "v5: " << v5 << endl; int *p6 = new int (1024); //shared_ptr<int> p6(new int(1024)); 正确做法 process(shared_ptr< int >(p6)); int v6 = *p6; cout << "v6: " << v6 << endl; /* 输出结果: 不可混用new和shared_ptr! in process use_count:2 v5: 1024 in process use_count:1 v6: -572662307 */ //Test Four //shared_ptr可以通过reset方法重置指向另一个对象,此时原对象的引用计数减一。 //shared_ptr<string> v1 = make_shared<string>("LSH"); cout << "test shared_ptr reset:" << endl; cout << "p1 cnt:" << v1.use_count() << endl; v1.reset( new string( "LSH Reset" )); cout << "p1 cnt:" << v1.use_count() << endl; } unique_ptr< int > CloneFunction( int p) { return unique_ptr< int >( new int (p)); } void process_unique_ptr(unique_ptr< int > up) { cout << "process unique ptr: " << *up << endl; } void Unique_PtrTest() { //Test One //unique_ptr对于所指向的对象,正如其名字所示,是 独占 的。 //所以,不可以对unique_ptr进行拷贝、赋值等操作,但是可以通过release函数在unique_ptr之间转移控制权。 cout << "test unique_ptr base usage:" << endl; unique_ptr< int > up1( new int (1024)); cout << "up1: " << *up1 << endl; unique_ptr< int > up2(up1.release()); cout << "up2: " << *up2 << endl; //unique_ptr<int> up3(up1); // 错误,不可进行拷贝操作 //up2 = up1; // 错误,不可进行拷贝操作 unique_ptr< int > up4( new int (1025)); up4.reset(up2.release()); cout << "up4: " << *up4 << endl; //Test Two //上述对于拷贝的限制,有两个特殊情况, //即unique_ptr可以作为函数的返回值和参数使用,这时虽然也有隐含的拷贝存在,但是并非不可行的。 cout << "test unique_ptr parameter and return value:" << endl; auto up5 = CloneFunction(1024); cout << "up5: " << *up5 << endl; process_unique_ptr(move(up5)); //cout<<"up5 after process: "<<*up5<<endl; //错误segmentfault } void Weak_PtrTest() { //weak_ptr一般和shared_ptr配合使用。 //它可以指向shared_ptr所指向的对象,但是却不增加对象的引用计数。 //这样就有可能出现weak_ptr所指向的对象实际上已经被释放了的情况。因此,weak_ptr有一个lock函数,尝试取回一个指向对象的shared_ptr。 cout << "test weak_ptr basic usage:" << endl; auto p10 = make_shared< int >(1024); weak_ptr< int > wp1(p10); assert (p10.use_count() == 1); cout << "p10 use_count: " << p10.use_count() << endl; //p10.reset(new int(1025)); // this will cause wp1.lock() return a false obj shared_ptr< int > p11 = wp1.lock(); assert (p11.use_count() == 2); if (p11) cout << "wp1: " << *p11 << " use count: " << p11.use_count() << endl; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗