C++ smart pointer智能指针详解

Q## introduction

解决的问题
  • 内存泄漏
  • 共享所有权指针的传播与释放,如多线程使用同一个对象的析构问题
几个智能指针
  • auto_ptr (C++ 11 deprecated)
  • unique_ptr:独占对象的所有权(没有引用计数,故性能好)
  • shared_ptr:共享对象的所有权(性能差)
  • weak_ptr:配合shared_ptr解决循环引用问题

shared_ptr

内存模型

![在这里插入图在这里插入图片描述

  • shared_ptr内部包含两个指针,一个指向对象,另一个指向control block,control block中包含一个reference count,一个weak count和一些其他数据
  • std::shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。再最后一个shared_ptr析构的时候,内存会自动被释放。
常用函数及基本用法
// 初始化
std::shared_ptr<int> p1(new int(1)); 
// 含义同上,但更推荐使用make_shared,因为它更高效
std::shared_ptr<int> p1 = make_shared<int>(1);	
std::shared_ptr<int> p2 = p1; 
std::shared_ptr<int> p3; 

// reset():重置shared_ptr 
// 如果reset不带参数,则将指针置空,如果指针唯一指向那个对象,还需要释放对象的内存
// 如果reset带参数,则将指针指向新对象,并将旧对象引用计数减一,或释放旧对象
p3.reset(new int(1));
if(p3) {
	cout << "p3 is not null";
}

// use_count():返回shared_ptr的强引用计数
// unique():若use_count的值为1,返回true,否则为false
std::shared_ptr<int> p1; 
p1.reset(new int(1)); 
std::shared_ptr<int> p2 = p1; 
// 引用计数此时应该是2 
cout << "p2.use_count() = " << p2.use_count()<< endl;

// get():返回对象的原始指针
// 不建议使用!
// 如果要使用请尽量不要保存get()的返回值;不要delete get()的返回值,会导致该对象被delete两次
shared_ptr<int> ptr = make_shared<int>(1);
int* p = ptr.get();

// 删除器:如果传入的对象没有析构函数,应该定义删除器并传入
// 如果管理的是动态数组,也应该指定删除器!!
void DeleteIntPtr(int *p) { 
	cout << "call DeleteIntPtr" << endl;
	delete p; 
}
int main() { 
	std::shared_ptr<int> p(new int(1), DeleteIntPtr); 
	//也可以写成lambda函数:
	std::shared_ptr<int> p2(new int[10], [](int *p) { 
		delete[] p});
	return 0; 
}
注意事项
  • 不要使用一个原始指针初始化多个shared_ptr
int* p = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr);		// error!
  • 通过shared_from_this返回this指针
class A{
public:
    shared_ptr<A> getself(){
        return shared_ptr<A>(this);
    }
    ~A(){
        cout << "destruction" << endl;
    }
};
int main() { 
    shared_ptr<A> ptr1(new A);
    shared_ptr<A> ptr2 = ptr1->getself();
    return 0; 
}

将会调用两次析构函数。正确的用法应该是:

class A: public std::enable_shared_from_this<A>{
public:
    shared_ptr<A> getself(){
        return shared_from_this();
    }
    ~A(){
        cout << "destruction" << endl;
    }
};

int main() { 
    shared_ptr<A> ptr1(new A);
    shared_ptr<A> ptr2 = ptr1->getself();
    return 0; 
}
  • 不要在函数实参中创建shared_ptr
  • 避免循环引用,解决办法见weak_ptr
class B;
class C;

class B{
public:
    shared_ptr<C> bc;
    ~B(){
        cout << "B destruction" << endl;
    }
};

class C{
public: 
    shared_ptr<B> cb;
    ~C(){
        cout << "C destruction" << endl;
    }
};

int main() { 
    // cicle reference
    shared_ptr<B>ptr1(new B);
    shared_ptr<C>ptr2(new C);
    {
        ptr1->bc = ptr2;
        ptr2->cb = ptr1;
    }
    cout << "main" << endl;
    return 0; 
}

循环引用导致ptr1和ptr2的引用计数都为2,在离开作用域之后,二者的引用计数都减为一,并不会发生析构
在这里插入图片描述

unique_ptr

与shared_ptr的区别
  • 不允许复制,只能通过move来移交所有权
unique_ptr<T> my_ptr(new T); // 正确 
unique_ptr<T> my_other_ptr = std::move(my_ptr); // 正确 
unique_ptr<T> ptr = my_ptr; // error
  • make_unique在c++14才加入
auto upw1(std::make_unique<Widget>()); 
  • unique_ptr可以指向数组
std::unique_ptr<int []> ptr(new int[10]); 
ptr[9] = 9; 
std::shared_ptr<int []> ptr2(new int[10]);	// error!
  • 指定删除器需要确定类型
std::shared_ptr<int> ptr3(new int(1), [](int *p){delete p;}); 
std::unique_ptr<int> ptr4(new int(1), [](int *p){delete p;}); // error
std::unique_ptr<int, void(*)(int*)> ptr5(new int(1), [](int *p){delete p;}); // right

weak_ptr

基本用法
// use_count():获取当前观察资源的引用计数
shared_ptr<int> sp(new int(10)); 
weak_ptr<int> wp(sp); 
cout << wp.use_count() << endl;		// 1

// expired():判断所观察对象是否已经被释放
shared_ptr<int> sp(new int(10)); 
weak_ptr<int> wp(sp); 
if(wp.expired()) 
	cout << "weak_ptr无效,资源已释放"; 
else
	cout << "weak_ptr有效";

// lock():如果weak_ptr已经过期,返回空的shared_ptr指针;
// 如果未过期,返回一个和当前weak_ptr指向相同的shared_ptr指针
// 因为weak_ptr没有重载* 和->,只能用lock取所指的对象
auto spt = gw.lock(); 
if(gw.expired()) { 
	cout << "gw无效,资源已释放";
}else {
	cout << "gw有效, *spt = " << *spt << endl; 
}

在这里插入图片描述

解决循环引用问题
class B;
class C;

class B{
public:
    weak_ptr<C> bc;	// 将shared_ptr修改为weak_ptr
    ~B(){
        cout << "B destruction" << endl;
    }
};

class C{
public: 
    shared_ptr<B> cb;
    ~C(){
        cout << "C destruction" << endl;
    }
};

int main() { 
    // cicle reference
    shared_ptr<B>ptr1(new B);
    shared_ptr<C>ptr2(new C);
    {
        ptr1->bc = ptr2;
        ptr2->cb = ptr1;
    }
    cout << "main" << endl;
    return 0; 
}

weak_ptr不会增加引用计数,故ptr2的引用计数仍然是1,在离开作用域之后,ptr2的引用计数减为0,会被析构
在这里插入图片描述

注意事项

weak_ptr使用前需检查合法性

weak_ptr<int> wp2;
{
     shared_ptr<int> sp_ok;
        shared_ptr<int> sp(new int(1)); //sp.use_count()==1
        wp2 = sp; //wp不会改变引用计数,所以sp.use_count()==1 
        sp_ok = wp2.lock(); //wp没有重载->操作符。只能这样取所指向的对象 
    }
    if(wp2.expired()) {
        cout << "shared_ptr is destroy" << endl; 
    } else { 
        cout << "shared_ptr no destroy" << endl; 
    }

在这里插入图片描述

安全性问题

⚠️所管理数据的线程安全性问题⚠️

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值