智能指针 (C++11)

shared_ptr

shared_ptr允许有多个指针指向同一个对象。
每个share_ptr都有一个关联的计数器,通常称为引用计数(reference count)。拷贝一个shared_ptr,计数器递增。
当指向的最后一个shared_ptr被销毁时,是调用析构函数来完成销毁工作的,析构函数会先递减它所指向的对象的引用计数,再检查引用计数值,如果引用计数变为0,那么shared_ptr析构函数就会销毁对象,并释放它占用的内存。

初始化
int *p = new int(30);
std::shared_ptr<int> a(p);//方式1
auto b = std::make_shared<int>(30);//方式2
std::shared_ptr<int> c(b);//方式3

指定删除器
当指针引用技术为0时,自动调用指定的删除器来释放内存

void DeleteIntPtr(int* p) {
	cout << "delete" << endl;
	delete p;
}
std::shared_ptr<int> p(new int(33), DeleteIntPtr);

//删除器可以是lambda表达式
std::shared_ptr<int> p(new int(33), [](int* p){delete p;});

p.reset();//out: delete

用shared_ptr管理动态数组时,必须指定删除器,因为shared_ptr的默认删除器不支持数组对象

std::shared_ptr<int> p(new int[10], [](int* p){ delete[] p; });

//可以将std::default_delete作为删除器,其内部调用delete
std::shared_ptr<int> p(new int[10], std::default_delete<int[]>);

//封装个方法
template<typename T>
shared_ptr<T> make_shared_array(size_t size){
	return shared_ptr<T>(new T[size], default_delete<T[]>() );
}
auto p = make_shared_array<int>(10);//使用
成员函数

use_count:返回引用计数的个数
unique:返回是否是独占所有权( 是否use_count 为 1)
swap:交换两个 shared_ptr 对象(即交换所拥有的对象)
reset:放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数减1
get:返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr sp(new int(1)); 其中sp 与 sp.get()是等价的

//use_count, reset, unique
std::shared_ptr<int> sp3(new int(22));
std::shared_ptr<int> sp4 = sp3;
printf("%d %d\n", sp3.use_count(), sp3.unique());    // 2 0
printf("%d %d\n", sp4.use_count(), sp4.unique());    // 2 0 
sp3.reset();
printf("%d %d\n", sp3.use_count(), sp3.unique());    // 0 0
printf("%d %d\n", sp4.use_count(), sp4.unique());    // 1 1

//swap
printf("%d %d\n", *r, *sp4);	//11 22
r.swap(sp4);
printf("%d %d\n", *r, *sp4);	//22 11
注意
  • 禁止纯指针给智能指针赋值或者拷贝构造
    int* a = new int(2);
    share_ptr<int> sp = a;//error
    
  • shared_ptr多次引用同一数据,会导致两次释放同一内存,所以无法用一个指针初始化多个shared_ptr
    int* pInt = new int[100];
    shared_ptr<int> sp1(pInt);
    // 一些其它代码之后…
    shared_ptr<int> sp2(pInt);//error
    
  • 使用shared_ptr包装this指针带来的问题,容易导致多次释放对象(出栈和智能指针析构)。解决方法:继承enable_shared_from_this并正确使用sp指针。
    class a: public std::enable_shared_from_this<a>{
    	shared_ptr<a> GetSelf(){
    		return shared_ptr<a>(this);//error
    		return shared_from_this();//accept, 返回this的shared_ptr
    	}
    }
    
  • shared_ptr循环引用导致内存泄露(两个类里面的智能指针互指,计数器永远无法变0释放)。引入weak_ptr可以解决这个问题
    //如下所示
    struct a;
    struct b;
    struct a{ std::shared_ptr<b>bptr;};
    struct b{ std::shared_ptr<a>aptr;};
    
    void test(){
    	std::shared_ptr<a> ap(new a);
    	std::shared_ptr<b> bp(new b);
    	ap->bptr = bp;
    	bp->aptr = ap;
    }
    
  • 在多线程程序中使用shared_ptr
    由于多线程同时访问智能指针,并将其赋值到其它同类智能指针时,可能发生两个线程同时在操作引用计数,而导致计数失败或无效等情况。引入weak_ptr可以解决这个问题

unique_ptr

unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。

unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。

std::unique_ptr<int> uptr(new int(10));
//std::unique_ptr<int> uptr2 = uptr;  //error 不能赋值
//std::unique_ptr<int> uptr2(uptr);  //error 不能拷贝
std::unique_ptr<int> uptr2 = std::move(uptr); //转换所有权
uptr2.release(); //释放

unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。

unique_ptr<int> clone(int p){
    unique_ptr<int> pInt(new int(p));
    return pInt;    // 返回unique_ptr
}

unique_ptr<int> ret = clone(5);

指定删除器的时候需要指定删除器类型

std::unique<int, void(*)(int*)> ptr(new int, [](int* p) {delete p;});
std::shared_ptr<int> p(new int(33), [](int* p){delete p;});//和shared_ptr对比一下
使用场景
  1. 为动态申请的资源提供异常安全保证.
    使用unique_ptr来管理动态内存,只要unique_ptr指针创建成功,其析构函数最终都会被调用。确保动态资源被释放。
  2. 返回函数内动态申请资源的所有权
    上面说的从函数中返回一个unique_ptr。
  3. 在容器中保存指针
    vector<unique_ptr<int>> vec;
    unique_ptr<int> p(new int(5));
    vec.push_back(std::move(p));    // 使用移动语义
    
  4. 管理动态数组
    unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5});
    p[0] = 0;   // 重载了operator[]
    
  5. 作为auto_ptr的替代品

weak_ptr

weak_ptr更像是shared_ptr的助手:
0、创建和销毁不会导致其引用的shared_ptr的计数器加减。
1、他不像其余三种,可以通过构造函数直接分配对象内存;他必须通过shared_ptr来共享内存。
2、没有重载opreator*和->操作符,也就意味着即使分配到对象,他也没法使用该对象
3、不主动参与引用计数,即,share_ptr释放了,那么weak_ptr所存的对象也释放了。
4、use_count()可以查看当前引用计数
5、expired()判断引用计数是否为空。
6、lock()函数,返回其引用的shared_ptr智能指针

成员函数
weak_ptr<int> wp;
void f() {
	if (wp.expired()) {//已被释放
		cout << "expired" << endl;
	}else {
		auto spt = wp.lock();
		cout << *spt << endl;
	}
}

int main(){
	{
		shared_ptr<int> sp(new int(10));
		wp = sp;
		f();//expired
		cout << wp.use_count() << endl; //1 
	}
	f();//10
	cout << wp.use_count() << endl; //0
}
解决返回this指针

前面shared_ptr用过。
因为tihs指针不能直接返回为shared_ptr…所以enable_shared_from_this中有一个weak_ptr,用来观测this智能指针,调用shared_from_this其实就是调用weak_ptr的lock(),会返回其观测的shared_ptr

class a: public std::enable_shared_from_this<a>{
	shared_ptr<a> GetSelf(){
		return shared_ptr<a>(this);//error
		return shared_from_this();//accept, 返回this的shared_ptr
	}
}
解决循环引用

只需将上述的情况其中一个改为weak_ptr

struct a;
struct b;
struct a{ std::shared_ptr<b>bptr;};
struct b{ std::weak_ptr<a>aptr;};

void test(){
	std::shared_ptr<a> ap(new a);
	std::shared_ptr<b> bp(new b);
	ap->bptr = bp;
	bp->aptr = ap;
}
//上面的操作,ap的引用计数是1.
//离开作用域后,ap的引用计数器会减为0,a被析构,bptr为1
//离开作用域后bp也减为1,b被析构。

猜你喜欢

转载自blog.csdn.net/tony__lin/article/details/83859652