boost 智能指针库

内存管理需要注意的点

  • 内存泄漏
  • 野指针
  • 访问越界

为了避免这些问题,智能指针采用RAII机制 —资源获取既初始化

  • 所有初始化操作移到对象的构造函数中
  • 所有的释放操作都放在对象的析构函数里
  • 适当的异常处理代码来应付对象构造期间丢出的异常(分配内存的时候)

好处:对象创建后,用户能开始正确使用对象,不用担心对象的有效性或者是否还要作进一步的初始化操作。

在这里插入图片描述

scoped_ptr 不能拷贝,赋值。

侯捷大师说过,源码面前,了无秘密

于是就去找一下源码,源码中将 拷贝赋值函数设置为 private,并且函数里面为空语句。
不适用于stl 类似的容器中

weak_ptr weak_ptr 可以说具有观察权。
准确的说不能算作一种智能指针,它的出现是用来弥补 share_ptr 的缺点,例如循环引用。
它有一个lock成员函数,可以将弱引用转化为 share_ptr。

其中最常用的就是share_ptr了,它也被收入到了c++ 11 标准中

  • share_ptr 用了设计模式中的 代理模式,用share_ptr 代理指针,所以它获得了指针相关的操作,比如解引用等等
  • 不可以使用变址操作符(因为指针越界大部分是因为变址操作符导致的)。
  • share_ptr 不需要手动的调用类似release 方法。
  • share_ptr 只会所有权转移 --rest()

share_ptr 源码摘要

template<class T>
class shared_ptr
{
	//创建一个持有空指针的share_ptr,use_count = 0 && get() == NULL
	shared_ptr() BOOST_NOEXCEPT : px( 0 ), pn() // never throws in 1.30+{}
//获得一个指向类型T的指针p的管理权,Y 必须能够转换为T类型	
template<class Y>  
explicit shared_ptr( Y * p ): px( p ), pn() // Y must be complete
//作用同上,增加了一个构造函数D,是一个仿函数对象,代表删除器
template<class Y, class D> 
shared_ptr( Y * p, D d ): px( p ), pn( p, d )

//析构函数:资源引用计数减1 同时判断引用计数为0,并且 管理的资源不为NULL,并且判断是否有删除器,如果有删除器,调用删除器,如果没有删除器,调用delete
~shared_ptr();
};

这里为什么要有删除器呢?
例如:fopen(),fclose() 操作打开的资源。这是delete 没用的。

构造函数获得管理权,
拷贝赋值操作 共享管理权

至于shared_ptr 的成员函数,这里就只说一下reset();
void reset();
template<class Y> void reset(Y* p);
template<class Y,class D> void reset(Y* p);

reset(): 如果该share_ptr 拥有某个管理权的话,它会将引用计数减1,如果资源计数器为0,并且指针不为NULL,删除原来共享的资源。将指针赋为 NULL

template void reset(Y* p); --转移管理权
首先判断该share_ptr 是否拥有管理权,如果有的话,,它会将引用计数减1,如果资源计数器为0,它会释放掉原来所指向的资源或指针,让他delete,然后它会将它内部的T 类型的指针,设置为参数 Y 的指针,这样就获得了 Y 类型 的 p 指针的管理权,然后将引用计数设为1。

shared_ptr 使用时出现的常见问题

1.shared_ptr 多次引用同一内存数据导致程序崩溃

No code , No BB


class Foo
{
public:

	Foo(string s) : m_s(s) {}
	~Foo()
	{
		cout << "Foo 开始析构" << endl;
	}
private:

		string m_s;
};
int main()
{
	Foo* pFoo = new Foo("test");

	shared_ptr<Foo> p1(pFoo);
	shared_ptr<Foo> p2(pFoo);


	return 0;
}

运行结果:
在这里插入图片描述
可以看到,将Foo() 析构了两次。

解决方案:
1.使用匿名 new操作,资源获取既初始化

shared_ptr<Foo> p1(new Foo("test"));

2.使用boost 工厂函数

2.shared_ptr 循环引用导致内存泄露

class B; // 前置声明

class A {
public:
	A()
	{
		cout << "A 构造" << endl;

	}
	~A()
	{
		cout << "A 析构" << endl;
	}
    	shared_ptr<B> ptr;

};

class B {
public:
	B()
	{
		cout << "B 构造" << endl;
	}
	~B()
	{
		cout << "B 析构 " << endl;
	}
    	shared_ptr<A> ptr;

};

int main()
{
        shared_ptr<A> pa(new A());
        shared_ptr<B> pb(new B());
        pa -> ptr = pb;     // 1
        pb -> ptr = pa;     //2 

	
	return 0;
}

运行结果:
在这里插入图片描述
结果没有调用析构函数。
什么原因呢?

当执行 1 时,pb 指针的引用计数加 1
当执行 2 时,pa 指针的引用计数加 1

离开主函数时,两个指针的引用计数减一,不为 0 ,所以不调用析构函数。

解决方法:
weak_ptr

1.从上面的例子可以看出,引用计数是一种很便利的内存管理,但是有一个缺点,那就是不能管理循环引用或自引用对象,然后就引入了 weak_ptr 。

2.它是与shared_ptr 同时使用的,它更像是shared_ptr 的助手而不是智能指针,因为它不具备普通指针的行为,没有重载operaotr * 和 -> 操作符。这是特意的。这样他就不能共享指针,不能操作资源,这正是它"弱"的原因,它最大的作用是协助shared_ptr 工作,像旁观者那样观察资源的使用情况

3.weak_ptr 可以从一个shared_ptr 或另外一个weal_ptr 构造,从而获得资源的观察权,但weak_ptr 并没有共享资源,它的构造并不会引起引用计数的增加,同事它的析构也不会引起引用计数的减少,它仅仅是观察者。

4.weak_ptr 的lock 成员函数 的返回值是一个shared_ptr 类型的指针。

make_shared< T >模板工厂函数

1.用于不需要使用删除器的情况下(仅仅使用 new /delete 进行内存分配和析构的类上面)
有数shared_ptr 显式的消除了delete 操作符的调用,因此使用该函数也可以显式的消除了 new 操作符的调用

2.调用该函数比直接创建shared_ptr 对象的方式快且高效,因为它内部仅分配一次内存,消除了shared_ptr 构造时的开销,建议在满足情况的基础上尽量使用该函数

3.它具有可变模板参数特性,如果c++ 编译器支持c++ 11 的可变参数模板特性,那么该工厂函数的参数数量没有限制,否则它只能接受最多 10 个参数被传递到 T 的构造函数参数中去。

猜你喜欢

转载自blog.csdn.net/qq_43701555/article/details/104950767