2026/4/17 1:13:02
网站建设
项目流程
南昌专业网站建设,如何修改wordpress模板栏目的属性,关于做书的网站,银行需要网站开发人员吗一、智能指针存在的意义智能指针主要解决以下问题#xff1a; #xff08;1#xff09;内存泄漏#xff1a;内存手动释放#xff0c;使用智能指针可以自动释放。 #xff08;2#xff09;共享所有权指针的传播和释放#xff0c;比如多线程使用同一个对象时析构问题。C里…一、智能指针存在的意义智能指针主要解决以下问题 1内存泄漏内存手动释放使用智能指针可以自动释放。 2共享所有权指针的传播和释放比如多线程使用同一个对象时析构问题。C里面有四个智能指针auto_ptr、share_ptr、unique_ptr、weak_ptr。其中后三个是C11支持的并且第一个已经在C11弃用所以重点讲解share_ptr、unique_ptr、weak_ptr。 它们的特点 1unique_ptr独占对象的所有权由于没有引用计数性能较好。 2share_ptr共享对象的所有权但性能略差。 3weak_ptr配合share_ptr解决循环引用问题。二、shared_ptrstd::shared_ptr使用引用计数每一个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候内存才会被释放。sharedd_ptr共享被管理的对象同一时刻可以有多个shared_ptr拥有对象的所有权当最后一个shared_ptr对象销毁时被管理对象自动销毁。简单的说shared_ptr实现包含了两个部分 1一个指向堆上创建的对象的裸指针 raw_ptr。 2一个指向内部隐藏的、共享的管理对象 shared_count_object。其中use_count是当前这个堆上对象被多少对象引用了简单来说就是引用计数。2.1、shared_ptr内存模型图片shared_ptr内部包含两个指针一个指向对象一个指向控制块。控制块包含一个引用计数、一个弱计数和其他数据比如删除器、分配器等。其中reference count会累计对象的使用者数量。代码语言javascriptAI代码解释std::shared_ptrint p1(new int(1)); std::shared_ptrint p2p1;上例中p1和p2的内存模型关系就是图片2.2、shared_ptr使用场景1使用智能指针可以自动释放占用的内存。代码语言javascriptAI代码解释// Buffer对象分配在堆上但能自动释放 shared_ptrBuffer bufmake_sharedBuffer(auto free memory); // Buffer对象分配在堆上但需要手动delete释放 Buffer *buf2new Buffer(free memory);2共享所有权指针的传播和释放。图片同样的数据但不同的业务处理不一样。使用shared_ptr智能指针可以减少内存拷贝因为有引入计数的存在当引入计数变为 0 时才真正去释放内存。2.3、shared_ptr的基本使用和常用函数1s.get()返回shared_ptr中保存的裸指针。 2s.reset(…)重置shared_ptr。reset()不带参数时若智能指针s是唯一指向该对象的指针则释放并置空。若智能指针s不是唯一指向该对象的指针则引用计数减一同时将s置为空。reset()带参数时若智能指针s是唯一指向该对象的指针则释放并指向新的对象。若智能指针s不是唯一指向该对象的指针则引用计数减一并指向新的对象。例如代码语言javascriptAI代码解释auto smake_sharedint(100); s.reset(new int(200));3s.use_count()返回shared_ptr的强引用计数。 4s.unique()若use_count为1返回true否则返回false。2.3.1、初始化 make_shared / reset通过构造函数、std::shared_ptr辅助函数和reset方法来初始化shared_ptr代码如下代码语言javascriptAI代码解释std::shared_ptrint p1(new int(1)); std::shared_ptrint p2p1; std::shared_ptrint p3; p3.reset(new int(1));应该优先使用make_shared来构造智能指针因为它更高效。代码语言javascriptAI代码解释auto p1make_sharedint(100); shared_ptrint p2make_sharedint(100); // 相当于 shared_ptrint p1(new int(100));不能将原始指针直接赋给一个智能指针。例如下面这种方法是错误的代码语言javascriptAI代码解释std::shared_ptrint pnew int(1);shared_ptr不能通过“直接将原始这种赋值”来初始化需要通过构造函数和辅助方法来初始化。对于一个未初始化的智能指针可以通过reset方法来初始化当智能指针有值的时候调用reset会引起引用计数减1。另外智能指针可以通过重载的bool类型操作符来判断。代码语言javascriptAI代码解释#include iostream #include memory using namespace std; int main() { std::shared_ptrint p1; p1.reset(new int(1)); std::shared_ptrint p2 p1; // 引用计数此时应该是2 cout p2.use_count() p2.use_count() endl; p1.reset(); cout p1.reset()\n; // 引用计数此时应该是2 cout p2.use_count() p2.use_count() endl; if(!p1) { cout p1 is empty\n; } if(!p2) { cout p2 is empty\n; } p2.reset(); cout p2.reset()\n; cout p2.use_count() p2.use_count() endl; if(!p2) { cout p2 is empty\n; } return 0; }2.3.2、获取原始指针 get()当需要获取原始指针时可以通过get方法来返回原始指针代码如下所示代码语言javascriptAI代码解释std::shared_ptrint ptr(new int(1)); int *p ptr.get(); // 不小心 delete p;谨慎使用get()的返回值如果不清楚其危险性则永远不要调用get()函数。 p.get()的返回值就相当于一个裸指针的值不合适的使用这个值上述陷阱的所有错误都有可能发生 遵守以下几个约定不要保存get()的返回值 无论是保存为裸指针还是shared_ptr都是错误的。保存为裸指针不知什么时候就会变成空悬指针保存为shared_ptr则产生了独立指针。不要delete p.get()的返回值 会导致对一块内存delete两次的错误。2.3.3、指定删除器如果用shared_ptr管理非new对象或是没有析构函数的类时应当为其传递合适的删除器。代码语言javascriptAI代码解释#include iostream #include memory using namespace std; void DeleteIntPtr(int *p) { cout Call DeleteIntPtrendl; delete p; } int main(int argc, char **argv) { shared_ptrint p(new int(1),DeleteIntPtr); return 0; }当p的引用计数为0时自动调用删除器DeleteIntPtr来释放对象的内存。删除器可以是一个lambda表达式上面的写法可以改为代码语言javascriptAI代码解释shared_ptrint p(new int(1),[](int *p){ cout Call DeleteIntPtrendl; delete p; });当使用shared_ptr管理动态数组时需要指定删除器因为shared_ptr的默认删除器不支持数据对象代码如下代码语言javascriptAI代码解释std::shared_ptrint p3(new int[10],[](int *p){delete [] p;});2.4、shared_ptr使用要注意的问题1不要用一个原始指针初始化多个shared_ptr。 错误示范如下代码语言javascriptAI代码解释int *ptrnew int; shared_ptrint p1(ptr); shared_ptrint p2(ptr);//逻辑错误2不要在函数实参中创建shared_ptr。 错误示范如下代码语言javascriptAI代码解释function(shared_ptrint(new int),g());因为C的函数参数的计算顺序在不同的编译器不同的约定下可能是不一样的一般是从右到左但也可能从左到右所以可能的过程是先new int然后调用g()如果恰好g()发生异常而shared_ptr还没有创建则int内存泄漏正确的写法应该是先创建智能指针代码如下代码语言javascriptAI代码解释shared_ptrint p1(new int); function(p1,g());3通过shared_from_this返回this指针。 不要将this指针作为shared_ptr返回回来因为this指针本质上是一个裸指针因此可能会导致重复析构如下例子代码语言javascriptAI代码解释#include iostream #include memory using namespace std; class MyClass { public: shared_ptrMyClass GetSelf() { return shared_ptrMyClass(this);//不要这样做 } MyClass() { cout MyClass() endl; }; ~MyClass() { cout ~MyClass() endl; }; }; int main() { shared_ptrMyClass sp1(new MyClass); shared_ptrMyClass sp2 sp1-GetSelf(); return 0; }运行后调用两次析构代码语言javascriptAI代码解释MyClass() ~MyClass() ~MyClass() free(): double free detected in tcache 2 已放弃 (核心已转储)在这个例子中由于用同一个指针this)构造了两个智能指针sp1和sp2而他们之间是没有任何关系的在离开作用域之后this将会被构造的两个智能指针各自析构导致重复析构的错误。正确返回this的shared_ptr的做法是让目标类继承std::enable_shared_from_this类然后使用基类的成员函数shared_from_this()返回this的shared_ptr如下所示代码语言javascriptAI代码解释#include iostream #include memory using namespace std; class MyClass: public std::enable_shared_from_thisMyClass { public: shared_ptrMyClass GetSelf() { return shared_from_this();//不要这样做 } MyClass() { cout MyClass() endl; }; ~MyClass() { cout ~MyClass() endl; }; }; int main() { shared_ptrMyClass sp1(new MyClass); shared_ptrMyClass sp2 sp1-GetSelf(); return 0; }执行结果代码语言javascriptAI代码解释MyClass() ~MyClass()4避免循环引用。 循环引用会导致内存泄漏。比如代码语言javascriptAI代码解释#include iostream #include memory using namespace std; class A; class B; class B { public: shared_ptrA aptr; B(); ~B(); private: }; B::B() { cout B() endl; } B::~B() { cout B is deleted endl; } class A { public: shared_ptrB bptr; A(); ~A(); private: }; A::A() { cout A() endl; } A::~A() { cout A is deleted endl; } int main() { shared_ptrA ap(new A); shared_ptrB bp(new B); ap-bptr bp; bp-aptr ap; cout main leave endl; // 循环引用导致ap bp退出了作用域都没有析构 return 0; }循环引用导致ap和bp的引用计数为2在离开作用域之后ap和bp的引用计数减为1并不回减为0导致两个指针都不会被析构产生内存泄漏。 解决的办法是把A和B任何一个成员变量改为weak_ptr。三、unique_ptr1unique_ptr是一个独占型的智能指针不能将其复制给另一个unique_ptr。 2unique_ptr可以指向一个数组。 3unique_ptr需要确定删除器的类型。3.1、unique_ptr是一个独占型的智能指针unique_ptr是一个独占型的智能指针它不允许其他的智能指针共享其内部的指针不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。下面的错误示例。www.dongchedi.com/article/7599372466490016280www.dongchedi.com/article/7599372650099999257www.dongchedi.com/article/7599372851132776984www.dongchedi.com/article/7599372306620039742www.dongchedi.com/article/7599372306619908670www.dongchedi.com/article/7599370698540368409www.dongchedi.com/article/7599370149677842969www.dongchedi.com/article/7599370761858023961www.dongchedi.com/article/7599370557410378302www.dongchedi.com/article/7599369686161244697www.dongchedi.com/article/7599370201301500441www.dongchedi.com/article/7599370761857761817www.dongchedi.com/article/7599371529427960344www.dongchedi.com/article/7599369580343001625www.dongchedi.com/article/7599335913373680190www.dongchedi.com/article/7599334077720150590www.dongchedi.com/article/7599334457882984984www.dongchedi.com/article/7599335431117259326www.dongchedi.com/article/7599334113941701182www.dongchedi.com/article/7599333889857307198www.dongchedi.com/article/7599332892518580761www.dongchedi.com/article/7599334507862786584www.dongchedi.com/article/7599332232163131928www.dongchedi.com/article/7599332409993544254www.dongchedi.com/article/7599331431303103000www.dongchedi.com/article/7599331421261939224www.dongchedi.com/article/7599331283516801561www.dongchedi.com/article/7599330823195591193www.dongchedi.com/article/7599236014061912601www.dongchedi.com/article/7599236952864326168www.dongchedi.com/article/7599233838816461337www.dongchedi.com/article/7599234108371354174www.dongchedi.com/article/7599232673466499646www.dongchedi.com/article/7599231381528871449www.dongchedi.com/article/7599230852278911550www.dongchedi.com/article/7599230129096933950www.dongchedi.com/article/7599224698819592766www.dongchedi.com/article/7599377947404861977