通过常规指针管理动态内存的难点及缺点:
(1)忘记释放动态内存: 使用new/malloc分配动态内存时,需要使用delete/free手动释放内存,但程序员容易忘记释放内存,从而产生内存泄露;
(2)动态内存释放时机不对: 在尚有指针引用内存的情况下,程序员释放了内存,从而产生空悬指针。典型的案例就是:假设在多线程编程中,线程A、B中分别有指针p1、p2指向对象object,若某个时刻线程A通过p1将object销毁了(释放了object占有的内存),那么线程B中的p2引用object就会产生内存错误。
程序员手动管理动态内存的困难,促使了智能指针的产生。智能指针可以自动隐式地释放动态内存,大大减少了内存错误,方便了动态内存的管理。有很多学者都对智能指针的源码进行了剖析,我将结合源码剖析分两篇博客对auto_ptr、unique_ptr、shared_ptr、weak_ptr依次进行总结。
auto_ptr根据博客https://blog.csdn.net/yanglingwell/article/details/56011576的源码剖析进行原理总结。
auto_ptr
auto_ptr中只有一个私有成员变量,即指向_Ty类型对象的指针_Myptr。
1、构造函数
auto_ptr有三种构造方式,一是通过传入指针来初始化_Myptr,二是通过同类型的auto_ptr对象来拷贝构造,但会利用release函数将原对象指针置为空,三是通过不同类型的auto_ptr对象来拷贝构造,也会利用release函数置空原对象指针。
2、拷贝赋值函数
拷贝赋值函数有两种,一是利用相同类型的auto_ptr对象进行拷贝赋值,二是利用不同类型的auto_ptr对象进行拷贝赋值。二者都会用release函数置空传入的对象指针并返回新的对象,再用reset函数赋值自身的指针成员_Myptr。
3、重载函数
auto_ptr本质上是一个类,为了让auto_ptr对象有像指针一样的行为,就需要重载*运算符和->运算符,这样auto_ptr对象就有了指针操作。
例如:
class A
{
public:
int m_A;
int m_B;
}
auto_ptr< int > p(new A(a,b));
“p->”可以访问A中的成员,“*p”可以得到指向对象的首地址。
4、析构函数
auto_ptr的析构函数比较简单,仅释放指针成员_Myptr持有的资源,即delete _Myptr。auto_ptr的析构函数保证了auto_ptr对象在生命期结束销毁时,可以自动释放其指向的内存资源,从而避免了内存泄露。
5、auto_ptr总结
auto_ptr是智能指针的初始版本,其设计原则为“严禁一物二主”。auto_ptr的设计中存在不合理之处,即提供了拷贝构造、拷贝赋值操作,却会置空原对象指针成员,这样显得拷贝毫无意义,而且在使用中极易误操作原对象,从而出现错误且很难发现。因此,现在的标准库已经不推荐使用auto_ptr了,但是可以作为智能指针的入门学习。
unique_ptr根据https://blog.csdn.net/qq_28114615/article/details/100528326的源码剖析进行原理总结。
unique_ptr
unique_ptr是auto_ptr的进化版本,也是一种独占式指针,但是合理性更强。
unique_ptr禁止了拷贝构造和赋值重载,但是比auto_ptr多了一个删除器。
1、_Unique_ptr_base
_Unique_ptr_base是unique_ptr的父类,用于管理删除器。
_Unique_ptr_base是一个类模板,有两种定义,一种是删除器为空的定义,一种是删除器不为空的定义。
删除器为空时,_Unique_ptr_base只有一个成员_Myptr(pointer类型),删除器不为空时,_Unique_ptr_base有两个成员_Myptr(pointer类型)和_Mydel(_Dx类型)。其中,_Myptr是管理的指针成员,_Mydel是删除器实例。删除器为空时,使用的是默认的删除器default_delete。
2、构造函数
unique_ptr的构造函数主要针对pointer类型的指针成员_Myptr。
(1)无参构造:用临时构造的pointer类型构造_Unique_ptr_base,初始化指针成员_Myptr为NULL;
(2)有参构造:有两种,一是传入pointer类型的指针对象_Ptr来构造_Mybase,二是传入_Ptr和删除器实例来构造;
(3)移动构造:用另一个相同类型或不同类型的unique_ptr对象、以及auto_ptr对象来右值构造,从而实现了资源的拥有权的转移,保证了资源永远只有一个拥有者,符合独占式设计原则 。
3、移动赋值函数
unique_ptr的赋值函数也是利用右值重载的的原则,从而实现所有权的转移,符合独占式设计原则。
4、资源交换函数swap
unique_ptr利用swap函数实现两个相同类型unique_ptr对象所管理资源的所有权交换。
5、重载函数
与auto_ptr一样,unique也提供了*运算符和->运算符的重载函数,从而使unique_ptr有指针的行为。
6、析构函数
当unique_ptr对象生命期结束时,会在析构函数中调用删除器,删除器就会释放所管理的内存资源。
7、unique_ptr总结
unique_ptr的功能与auto_ptr类似,是一种独占式智能指针。unique_ptr的实现比较复杂,但是合理性更强。unique_ptr利用移动构造函数和移动赋值函数来实现资源的转移,即通过右值引用方式来销毁原对象,并把内存资源的所有权转移给新对象。原对象销毁后,程序员将无法再对原对象进行操作,这也避免了auto_ptr的缺点,因此,现在C++标准库中推荐使用unique_ptr。
unique_ptr与auto_ptr的另一个不同点是:auto_ptr直接在析构函数中释放内存资源;unique_ptr中定义了一个删除器,会在析构函数中调用删除器,再通过删除器释放资源。