哈尔滨铁路局建设网站,广告设计网站官网,做个公司网站一般需要多少钱,ip代理网址文章目录1. 介绍1.1 动态内存与智能指针2. 使用2.1 创建2.2 使用3. 原理3.1 RAII3.2 像指针一样使用3.3 支持智能指针对象拷贝auto_ptrRAII4. 标准库中的智能指针4.1 unique_ptr模拟实现4.2 shared_ptr引用计数模拟实现定制删除器4.3 weak_ptrshared_ptr造成的循环引用问题与sh…
文章目录1. 介绍1.1 动态内存与智能指针2. 使用2.1 创建2.2 使用3. 原理3.1 RAII3.2 像指针一样使用3.3 支持智能指针对象拷贝auto_ptrRAII4. 标准库中的智能指针4.1 unique_ptr模拟实现4.2 shared_ptr引用计数模拟实现定制删除器4.3 weak_ptrshared_ptr造成的循环引用问题与shared_ptr的关系模拟实现5. 常见问题1. 介绍
到目前为止我们编写的程序中所使用的对象都有着严格定义的生存期
全局对象程序启动时分配在程序结束时销毁。局部对象当我们进入其定义所在的程序块时被创建在离开块时销毁。局部static对象在第一次使用前分配在程序结束时销毁。
我们的程序到目前为止只使用过静态内存或栈内存
静态内存保存局部static对象、类static数据成员以及定义在任何函数之外的变量。栈内存保存定义在函数内的非static对象。分配在静态或栈内存中的对象由编译器自动创建和销毁。对于栈对象仅在其定义的程序块运行时才存在static对象在使用之前分配在程序结束时销毁。
除了静态内存和栈内存每个程序还拥有一个内存池。这部分内存被称作自由空间free store或堆heap。程序用堆来存储动态分配dynamically allocate的对象。动态对象的生存期由程序来控制也就是说当动态对象不再使用时我们的代码必须显式地销毁它们。
1.1 动态内存与智能指针
除了局部和static对象外C还支持动态分配对象。动态分配的对象的生存期与它们在哪里创建是无关的只有当显式地被释放时这些对象才会销毁。动态对象的正确释放是编程中极其容易出错的地方。为了更安全地使用动态对象标准库定义了两个智能指针类型来管理动态分配的对象。当一个对象应该被释放时指向它的智能指针可以确保自动地释放它。
在C中动态内存的管理是通过一对运算符来完成的
new在动态内存中为对象分配空间并返回一个指向该对象的指针我们可以选择对对象进行初始化delete接受一个动态对象的指针销毁该对象并释放与之关联的内存。
动态内存的使用很容易出问题
内存泄漏有时我们会忘记释放内存在这种情况下就会产生内存泄漏非法指针有时在尚有指针引用内存的情况下我们就释放了它在这种情况下就会产生引用非法内存的指针。
C11为了更容易同时也更安全地使用动态内存新的标准库提供了两种智能指针smart pointer类型来管理动态对象。智能指针的行为类似常规指针重要的区别是它负责自动释放所指向的对象。新标准库提供的这两种智能指针的区别在于管理底层指针的方式
shared_ptr允许多个指针指向同一个对象unique_ptr则“独占”所指向的对象。
标准库还定义了一个名为weak_ptr的伴随类它是一种弱引用指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。
智能指针是一个类它能够管理真正的指针引用的内存同时它还能像指针一样被使用就像仿函数函数对象能像函数一样被使用一样。
2. 使用
下面以shared_ptr为例。
2.1 创建
类似vector等容器智能指针也是模板。因此当我们创建一个智能指针时必须提供额外的信息指针可以指向的类型。与vector一样我们在尖括号内给出类型之后是所定义的这种智能指针的名字。
shared_ptrint p1; // 指向int的智能指针
shared_ptrstring p2; // 指向string的智能指针2.2 使用
智能指针的使用方式与普通指针类似。解引用一个智能指针返回它指向的对象。
int main()
{shared_ptrint p1(new int(1));cout p1 endl;cout *p1 endl;return 0;
}输出
0x6000031d0030
1虽然智能指针是一个类但是其中重载了操作指针的运算符用起来就像指针一样。还可以像指针一样访问类中的成员
class Person
{
public:Person(const string name): _name(name){}const string GetName(){return _name;}
private:string _name;
};
int main()
{shared_ptrPerson p(new Person(小明));cout p-GetName() endl;return 0;
}输出
小明使用上非常简单就像真正的指针一样下面将讨论智能指针管理资源的原理。
3. 原理
智能指针的实现必须解决下面三个问题
RAII将资源交给对象的生命周期管理即构造对象时开始获取管理资源析构对象时释放资源像真正的指针一样使用支持智能指针对象的拷贝。
其中最容易实现的是像指针一样使用只要重载使用指针的运算符即可。其次是实现RAII最后是智能指针对象的拷贝。
3.1 RAII
RAIIResource Acquisition Is Initialization资源获取即初始化由Bjarne StroustrupC之父提出。是一种将资源的生命周期绑定到对象的生命周期的 C 编程技术它有助于避免资源泄漏并简化错误处理。这是设计智能指针核心思想。
RAII在智能指针上的体现智能指针在构造时获取一个原始指针并在析构时释放它
int main()
{shared_ptrint p(new int(1)); // 构造时获取内存cout *p endl; // 访问内存// 出了作用域,自动释放内存return 0;
}RAII是一种利用对象生命周期来管理资源的技术它的意义在于
避免资源泄漏因为对象在析构时会自动释放所占用的资源。简化代码因为不需要手动管理资源的分配和释放。增强异常安全性因为即使发生异常对象也会被正常析构。
RAII是C中一种非常重要和实用的编程范式它体现了C的设计哲学让编译器帮助我们做更多的事情。
3.2 像指针一样使用
下面将用一个自定义智能指针SmartPtr为例
templateclass T
class SmartPtr
{
public:SmartPtr(T* ptr nullptr) // 构造时接管内存: _ptr(ptr){}~SmartPtr() // 析构时释放内存{cout delete: _ptr endl; // 提示语句delete _ptr;}// 重载操作符T operator*(){return *_ptr;}T* operator-(){return _ptr;}
private:T* _ptr;
};
int main()
{SmartPtrint p(new int(1));cout *p endl;return 0;
}其中重载了*和-运算符使得使用这个类就像使用指针一样。智能指针是一个模板类以能够管理任何类型的指针引用的内存如果模板参数是一个有公有成员的类那么还能使用-访问其成员。 当智能指针未初始化时赋予nullptr缺省值。 3.3 支持智能指针对象拷贝
上面实现的智能指针SmartPtr是极不完善的如果想实现拷贝构造和拷贝赋值
int main()
{SmartPtrint p1(new int(1));SmartPtrint p2(p1); // 拷贝构造SmartPtrint p3(new int(2));SmartPtrint p4(new int(2));p4 p3; // 拷贝赋值return 0;
}输出
delete:0x600003c84030
delete:0x600003c84030错误Clion
malloc: *** error for object 0x600003c84030: pointer being freed was not allocated造成程序崩溃的原因是在这个类中没有实现拷贝构造函数和拷贝赋值函数而编译器默认生成的全都是对内置类型的浅拷贝值拷贝相当于p1和p2、p3和p4共同管理同一块空间。当出了p1的作用域后调用析构函数释放空间p2再次调用析构函数时导致这块已经被释放的空间再次被释放。p3和p4同理。 要解决浅拷贝造成的二次析构问题就要实现深拷贝的拷贝构造函数和拷贝赋值函数吗 答案是否定的智能指针的功能需求是模拟指针的使用本质是帮指针托管资源那么指针的拷贝或赋值操作就相当于两个指针指向同一块内存空间。资源管理权转移通过不负责任的拷贝会导致被拷贝对象悬空。虽然资源能得到释放但是会造成垂悬指针。智能指针将内存资源的管理和对象的生命周期绑定在一起如果只是像上面一样简单地满足RAII那么一定会发生二次析构的问题因为创建的智能指针对象一定会调用析构函数且不论程序是否正常结束。 程序正常结束对象出了作用域调用析构函数 程序不正常结束例如抛异常跳转到catch块相当于跳转到另一个函数的栈帧中也相当于出了作用域依然调用析构函数。 下面以标准库中C98智能指针auto_ptr为例。
auto_ptr
出现多次析构问题的本质是同一块内存空间被多个对象通过管理如果将资源的管理权只交给一个对象就不会出现多次析构问题。
int main()
{auto_ptrint p1(new int(1));auto_ptrint p2(p1);auto_ptrint p3(new int(2));auto_ptrint p4(new int(2));p3 p4;return 0;
}然而将一个对象对资源的管理权转移后就意味着这个对象再对资源访问是一个非法操作程序会因此崩溃。如果让不熟悉auto_ptr原理的人使用因为拷贝操作而造成非法指针或内存泄漏是有可能的而这也是致命的错误因此许多公司明文规定禁止auto_ptr的使用进而用C11的unique_ptr和shared_ptr取代。
通过模拟实现auto_ptr理解其原理以更好地理解unique_ptr和shared_ptr。同样地实现auto_ptr也需要满足三点其中像指针一样使用仍然不变。
RAII 析构函数需要对它管理的指针判空只有指针非空时才能对其进行释放资源操作释放资源以后对其置空。 拷贝构造函数用传入对象管理的内存资源来构造当前对象并将传入对象管理资源的指针置空。 拷贝赋值函数先将当前对象管理的资源释放然后再接管传入对象管理的资源最后将传入对象管理资源的指针置空。
namespace xy
{templateclass Tclass auto_ptr{public:auto_ptr(T* ptr nullptr):_ptr(ptr){}~auto_ptr(){if (_ptr ! nullptr){cout delete: _ptr endl;delete _ptr;_ptr nullptr;}}auto_ptr(auto_ptrT ap):_ptr(ap._ptr){ap._ptr nullptr; // 管理权转移后置空ap}auto_ptr operator(auto_ptrT ap){if (this ! ap){delete _ptr; // 释放自己管理的资源_ptr ap._ptr; // 接管ap对象的资源ap._ptr nullptr; // 管理权转移后置空ap}return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
}测试
class A
{
public:A(const int a 0): _a(a){}~A(){}int _a;
};
int main()
{xy::auto_ptrA ap1(new A);ap1-_a;cout ap1-_a endl;xy::auto_ptrA ap2(ap1); // ap2接管ap1的资源ap2-_a;//cout ap1-_a endl; // errorcout ap2-_a endl;xy::auto_ptrA ap3(new A);ap3 ap2; // ap3接管ap2的资源// cout ap2-_a endl; // errorap3-_a;cout ap3-_a endl;return 0;
}输出
1
2
3
delete: 0x6000014a0030对于auto_ptr当一个智能指针接管另一个智能指针管理的资源后原来的指针已经解除对资源的引用是一个悬垂指针。如果对其进行任何访问操作会造成程序崩溃例如main()中被注释的语句。
下面介绍标准库中的两种智能指针并通过模拟实现理解它们的原理。
4. 标准库中的智能指针
库中的智能指针并不是凭空出世的在此之前boost社区对auto_ptr进行改良推出了几种智能指针。
Boost 社区是一个提供 C 库的开源项目Boost 大概是最重要的第三方C 库。其作者有很多是C 标准委员会的成员。Boost 的很多子库后来都成为C 的标准库。它包括了很多高质量和实用的库其中就有智能指针库。
Boost 的智能指针库提供了六种智能指针模板分别是 scoped_ptr、scoped_array、shared_ptr、shared_array、weak_ptr 和 intrusive_ptr。它们和 C11 的智能指针有一些相似之处也有一些不同之处。比如Boost 的 shared_ptr 和 weak_ptr 可以用于数组而 C11 的不可以而 C11 的 unique_ptr 可以使用移动语义而 Boost 的 scoped_ptr 不可以。Boost 社区对 C 标准的发展也有一定的影响比如 C11 的 shared_ptr 和 weak_ptr 就是基于 Boost 的实现。
4.1 unique_ptr
C98的auto_ptr因为拷贝和赋值操作而造成内存泄漏和悬垂指针的问题而饱受诟病C11引入的unique_ptr则粗暴地砍掉了它的拷贝和赋值功能。这通过C11引入的关键字delete的新功能实现。 在C11之前可以通过将构造函数和拷贝赋值函数私有声明实现。 模拟实现
模拟实现的过程去除auto_ptr中拷贝和赋值的函数。
namespace xy
{templateclass Tclass unique_ptr{public:unique_ptr(T* ptr nullptr):_ptr(ptr){}~unique_ptr(){if (_ptr ! nullptr){cout delete: _ptr endl;delete _ptr;_ptr nullptr;}}unique_ptr(auto_ptrT ap) delete;unique_ptr operator(auto_ptrT ap) delete;T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
}
int main()
{xy::unique_ptrint uqp1(new int);xy::unique_ptrint uqp2(new int);uqp2 uqp1; // errorreturn 0;
}对于以上赋值语句输出Clion
malloc: *** error for object 0x6000019a4030: pointer being freed was not allocated
malloc: *** set a breakpoint in malloc_error_break to debug
delete:0x6000019a4030
delete:0x6000019a4030C98中delete的意思是不让编译器自动生成默认函数而C11为了实现这个智能指针赋予delete一个新功能不允许调用。 4.2 shared_ptr
shared_ptr就是支持正常拷贝的auto_ptr。shared_ptr和其他智能指针的主要区别是它支持共享所有权也就是说多个shared_ptr对象可以拥有同一个资源而不会造成内存泄漏或悬空指针。其他智能指针如unique_ptr或weak_ptr只能有一个所有者或不能控制资源的生命周期。
shared_ptr通过一个指针保持对一个对象的共享所有权。多个shared_ptr对象可以拥有同一个对象。当以下情况之一发生时对象被销毁并释放其内存
拥有该对象的最后一个shared_ptr被销毁通过reset()函数将shared_ptr赋值为另一个指针。
引用计数
auto_ptr转移资源后造成内存泄漏和悬垂指针的主要原因就是每个auto_ptr智能指针对象管理的资源是各自独立的非此即彼。shared_ptr共享同一个资源内存资源只在最后一个智能指针解除引用时释放这样就不会造成资源被单方面地接管造成的问题。 引用计数使得一个空间可以被多个对象管理当引用计数为0时说明已经没有智能指针管理这块内存空间了此时才能释放资源弥补了auto_ptr的缺陷。要知道引用计数的值只需要调用shared_ptr的成员函数use_count()即可。
int main()
{shared_ptrint sp1(new int(1));shared_ptrint sp2(sp1);*sp1 10; // 拷贝后旧指针管理的资源依然能访问cout sp1.use_count() endl;shared_ptrint sp3(new int(0));shared_ptrint sp4(new int(2));sp3 sp4;cout sp3.use_count() endl; // 赋值后旧指针管理的资源依然能访问return 0;
}输出
2
2模拟实现 增加count成员变量表示引用计数 构造函数当获取到资源则设置count1表示当前只有一个智能指针对象管理此资源 拷贝构造函数将传入的智能指针对象中的count表示新增了一个管理者 拷贝赋值函数将本智能指针的count--表示解除对当前资源的引用然后再将传入的智能指针对象中的count表示管理新的资源 析构函数count--表示解除对当前管理资源的引用如果count0则释放资源 重载*和-运算符使shared_ptr对象具有指针一样的行为。 其中operator的重载需要注意两个问题 内存泄漏赋值时要把自己的引用计数给对方赋值代表对方要共同接管自己管理的资源所以对方的引用计数也要-1自我赋值本质也会造成内存泄漏自我赋值后资源的管理权并未发生变化但是引用计数却1了到真正最后一个对象时引用计数仍不为0如果自我赋值1次那就是1造成资源不能释放内存泄漏。 namespace xy
{templateclass Tclass shared_ptr{public:shared_ptr(T* ptr nullptr):_ptr(ptr), _pcount(new int(1)){}~shared_ptr(){if (--(*_pcount) 0){if (_ptr ! nullptr){cout delete: _ptr endl;delete _ptr;_ptr nullptr;}delete _pcount;_pcount nullptr;}}shared_ptr(shared_ptrT sp):_ptr(sp._ptr), _pcount(sp._pcount){(*_pcount);}shared_ptrT operator(shared_ptrT sp){if (_ptr ! sp._ptr) // 管理同一资源的智能指针赋值无意义{if (--(*_pcount) 0) // 将管理的资源的引用计数-1{cout delete: _ptr endl;delete _ptr;delete _pcount;}_ptr sp._ptr; // 与传入的智能指针共享资源_pcount sp._pcount; // 将自己的引用计数和传入的智能指针同步(*_pcount); // 引用计数1,表示自己是新增的管理者}return *this;}// 获取引用计数int use_count(){return *_pcount;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;int* _pcount; // 引用计数};
}
int main()
{xy::shared_ptrint sp1(new int(1));cout sp1.use_count() endl;xy::shared_ptrint sp2(sp1);cout sp1.use_count() endl;cout sp2.use_count() endl;xy::shared_ptrint sp3(new int(0));cout sp3.use_count() endl;xy::shared_ptrint sp4(new int(2));sp3 sp4;cout sp3.use_count() endl;cout sp4.use_count() endl;return 0;
}输出
1
2
2
1
delete:0x6000007f4050
2
2
delete:0x6000007f4070
delete:0x6000007f4030注意
shared_ptr中的引用计数是存放在堆区的因为这样可以让所有指向同一个对象的shared_p。如果引用计数在栈区那么当一个shared_ptr改变指向或者离开作用域时就无法通知其他shared_ptr更新引用计数了。因此引用计数也不能是静态成员每个类型实例化的智能指针对象时共用静态成员这会导致管理相同资源的对象和管理不同资源的对象共用同一个引用计数。
由于在堆区的引用计数和同一类型的智能指针是绑定在一起的当智能指针释放资源时也需要释放引用计数占用的内存。
定制删除器
实际上不是所有的对象都是new出来的也可能是new[]因此释放对象的资源也可能是delete[]。例如
当你想在释放对象时执行一些额外的操作例如关闭文件、释放资源、记录日志等。当你想使用一个不同于delete的函数来销毁对象例如free、fclose、Release等。当你想管理一个不是通过new分配的对象例如一个栈上的对象或一个全局变量。当你想管理一个不是单个对象而是一个数组或容器的对象。
定制删除器可以让你更灵活地控制shared_ptr如何管理和释放它所指向的对象。
假设你想管理一个打开的文件但是你不能使用delete来关闭它而是使用fclose
#include iostream
#include memory
#include cstdioint main()
{// 创建一个shared_ptr管理一个打开的文件// 使用fclose作为定制删除器std::shared_ptrFILE file(fopen(test.txt, w), fclose);// 写入一些内容到文件fputs(Hello world, file.get());// 当file离开作用域时会调用fclose来关闭文件
}这样就可以避免使用delete来释放一个不是通过new分配的对象从而导致危险行为。shared_ptr在实例化对象时有两个参数
template class U, class D
shared_ptr (U* p, D del);其中
p需要让智能指针管理的资源。del删除器这个删除器是一个可调用对象比如函数指针、仿函数、lambda表达式以及被包装器包装后的可调用对象。 实际上删除器就是一个被工具封装的动作这个动作就是用特定的方式释放资源。 总的来说当智能指针管理的资源不是通过new出来的时候就需要用对象类型和定制删除器构造智能指针。
例如可以传入一个lambda表达式作为定制删除器
int main()
{shared_ptrint sp1(new int[5], [](int* ptr){cout delete[]: ptr endl;delete[] ptr;});shared_ptrFILE sp2(fopen(test.cpp, r), [](FILE* ptr){cout fclose: ptr endl;fclose(ptr);});return 0;
}lambda表达式在定制删除器中的应用体现了lambda表达式的内联属性。 unique_ptr没办法用lambda表达式因为它是一个对象unique_ptr必须传模板参数类型。 库中的实现比较复杂因为要实现模板以满足各种定制器的类型。但总而言之就是根据实际情况定制一个类按照特定方式释放资源在创建对象的时候将这个特定的动作作为参数。
4.3 weak_ptr
shared_ptr造成的循环引用问题
shared_ptr解决了auto_ptr可能造成内存泄漏和悬垂指针的问题但是在少数情况下shared_ptr也会造成内存泄漏即循环引用问题。
循环引用是指当一个对象或者一个单元格内的公式直接或间接地引用了自己或者另一个对象。这样会导致内存泄漏或者计算错误。也就是说如果两个shared_ptr指针互相引用那么它们的引用计数永远不会为零也就无法释放内存。
例如
struct Node
{std::shared_ptrNode _next;std::shared_ptrNode _prev;~Node(){cout ~Node endl;}
};
int main()
{std::shared_ptrNode n1(new Node);std::shared_ptrNode n2(new Node);// 循环引用n1-_next n2;n2-_prev n1;return 0;
}为了share_ptr可以使用Node所以Node的两个指针都是shared_ptrNode类型的。 其中两个赋值语句造成了两个结点对象n1和n2循环引用 n1 和 n2 两个 Node 对象通过 _next 和 _prev 成员变量相互引用导致它们的引用计数永远不为零从而无法被销毁。 死循环资源只有在引用计数为1时才能被销毁。左边资源只有当右边的_prev释放以后引用计数才为0而右边资源只有当左边的_next释放以后引用计数才为0。 解决这个问题的一种方法是使用 std::weak_ptr 来代替其中任意一个方向上的 std::shared_ptr。
std::weak_ptr 是一种智能指针它用来解决 std::shared_ptr 循环引用的问题。它不会增加所指向对象的引用计数因此不会影响对象的销毁。
对于上面的例子造成问题的本质是引用计数永不为0那么只要将其中一个智能指针改为weak_ptr即可
struct Node
{std::shared_ptrNode _next;std::weak_ptrNode _prev; // 将_prev用weak_ptr管理~Node(){cout ~Node endl;}
};与shared_ptr的关系
weak_ptr是为了配合shared_ptr而引入的一种智能指针它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期。也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造它的构造和析构不会引起引用记数的增加或减少。弱引用能检测到所管理的对象是否已经被释放从而避免访问非法内存。 也就是说weak_ptr是为了弥补shared_ptr循环引用而生的它没有RAII的特性不直接管理资源只是shared_ptr的跟班这也是weak_ptr支持使用shared_ptr构造的原因。 在使用上weak_ptr支持指针所有的操作。它不是一个功能型的智能指针而是辅助型它的使命是解决shared_ptr造成的循环引用问题。 与 shared_ptr 不同weak_ptr 不能直接访问所指向的对象。要访问对象需要先调用 lock() 方法将其转换为 shared_ptr。如果所指向的对象已经被销毁则 lock() 方法返回空指针。
模拟实现
构造函数无参构造拷贝构造支持参数是shared_ptr类型和本身类型构造同时接管shared_ptr管理的资源但不增加引用计数拷贝赋值同上。智能指针一般有get()接口所以返回指针时可以调用像指针一样
它是一个辅助型指针在shared_ptr内部实现。
namespace xy
{templateclass Tclass weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptrT sp):_ptr(sp.get()){}weak_ptr operator(const shared_ptrT sp){_ptr sp.get();return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
}5. 常见问题 为什么需要智能指针 智能指针是一种用来管理在堆上分配的内存的工具。它将普通的指针封装为一个栈对象当栈对象的生存周期结束后会在析构函数中释放掉申请的内存从而防止内存泄漏。智能指针还可以防止忘记调用delete释放内存和程序异常进入catch块忘记释放内存。
C11中最常用的智能指针类型为shared_ptr它采用引用计数的方法记录当前内存资源被多少个对象共享。 什么是引用计数 是一种内存管理技术是指将资源可以是对象、内存或磁盘空间等等的被引用次数保存起来当被引用次数变为零时就将其释放的过程。 RAII是什么 RAII是Resource Acquisition Is Initialization的简称中文翻译为“资源获取即初始化”。它是C语言中的一种管理资源、避免泄漏的良好方法。它的原理是在构造函数中申请分配资源在析构函数中释放资源。 智能指针的发展历史 C98中产生了第一个智能指针auto_ptr但它存在较多问题。Cboost给出了更加实用的scoped_ptr和shared_ptr和weak_ptr。C11标准引入了unique_ptr和shared_ptr和weak_ptr其中unique_ptr对应的是boost中的scoped_ptr。
智能指针技术经过20多年的发展特别是C11标准引入shared_ptr和unique_ptr之后趋于成熟。 C有哪些智能指针它们之前的区别和使用场景 C11中推出了三种智能指针unique_ptr、shared_ptr和weak_ptr同时也将auto_ptr置为废弃。 unique_ptr是独占资源所有权的指针当我们独占资源的所有权的时候可以使用unique_ptr对资源进行管理——离开unique_ptr对象的作用域时会自动释放资源。这是很基本的RAII思想。 shared_ptr是共享资源所有权的指针。它使用引用计数来跟踪共享对象的引用数量。当引用计数变为0时对象被销毁。 weak_ptr是共享资源的观察者需要和shared_ptr一起使用不影响资源的生命周期。它可以解决循环引用问题。
智能指针有一些自己的问题比如循环引用、性能开销、处理数组、线程安全等在使用智能指针前都需要详细了解。 模拟实现简易的智能指针例如shared_ptr。 见4.2。 什么是循环引用如何解决原理 循环引用是指两个或多个对象相互引用导致它们之间形成一个循环。这会导致一些问题比如在使用引用计数的垃圾回收机制中循环引用的对象永远无法被释放。
解决循环引用的一种方法是使用弱引用即不增加对象的引用计数因此不会影响垃圾回收。例如在C中可以使用weak_ptr来解决shared_ptr之间的循环引用问题。当判断是否为无用对象时仅考虑强引用计数是否为0不关心弱引用计数的数量。