C++11内存管理优化:智能指针

内存管理优化

C++11提供智能指针来帮助管理内存,智能指针可以自动删除分配的内存
C++11提供三种智能指针:std::shared_ptrstd::unique_ptrstd::weak_ptr使用时需要引用头文件memory

C++11之前的auto_ptr

C++11之前的auto_ptr设计上有很多坑,若通过cory构造和 “=” 操作符赋值它们,被拷贝的指针会变成null ,而复制所得的指针将取得资源的唯一控制权。就轻轻松松把资源抢过来了。(太坏了)抢走了以后如果再去析构原来的内存肯定会有问题的。
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

所以C++11不建议使用auto_ptr,使用std::shared_ptr,std::unique_ptr这两个指针能实现auto_ptr的功能而且很安全

shared_ptr共享的智能指针

std::shared_ptr使用引用计数来实现内存共享的功能,当最后一个指向此内存的指针离开作用域以后,智能指针计数为0,这个内存才会被释放。

使用方式

  • 初始化方式:
	std::shared_ptr<int> p(new int(1));    // 构造函数初始化
	std::shared_ptr<int> p2 = p;
	
	std::shared_ptr<int> perr = new int(1); // 错误 不能直接赋值
	
	std::shared_pte<int> p3;
	p3.reset(new int(i));    // reset方法初始化

智能指针不能直接赋值初始化,需要通过构造函数初始化,或者reset方法初始化。

  • 获取原始指针方式:
	int* org = p.get();
  • 指定删除器:
	void Delete(int* p)
	{
		delete p;
	}
	std::shared_ptr<int> p(new int, Delete); 
	
	// 可以直接使用lambda表达式
	std::shared_ptr<int> p(new int, [](int* p){ deletw p;});

注意问题

  • 不能使用一个原始指针初始化多个shared_ptr
  • 不要在函数实参中创建shared_ptr,因为不同环境函数调用约定以及函数的压栈顺序不同
  • 通过shared_from_this()返回this指针,不要将this指针作为shared_ptr返回,this指针本质是一个裸指针,可能会造成重复析构
	struct A
	{
	    shared_ptr<A> getSelf()
	    {
	        return shared_ptr<A>(this); 
	    }
	};
	int main()
	{
	    shared_ptr<A> sp1(new A);
	    shared_ptr<A> sp2 = sp1->getSelf();
	    shared_ptr<A> sp3 = sp1;
	    return 0;
	}

上面的代码用A构造一个智能指针sp1,但是sp2是用同一个(this)指针构造两个智能指针,两个智能指针互相独立有各自的引用计数,如果离开作用域以后,各自的引用计数都-1,就会调用两次A的析构函数,然后析构同一块内存2次。。
对比sp3,sp3是跟sp1共享同一个智能指针体系,使用计数跟sp1是相同的。

可以看到三个指针指向同一个地址,但是指针引用计数不一样。
在这里插入图片描述

正确返回this指针的做法是,让目标类通过派生std::enable_shared_from_this类,使用成员函数shared_from_this返回this指针。

	class A :public std::enable_shared_from_this<A>
	{
	public:
	
	    std::shared_ptr<A> getSelf()
	    {
	        return shared_from_this();
	    }
	};
	int main()
	{
	    shared_ptr<A> sp1(new A);
	    shared_ptr<A> sp2 = sp1->getSelf();
	    shared_ptr<A> sp3 = sp1;
	}

这样就可以看到三个指针是同一个引用计数系统,enable_shared_from_this模板类里面是有一个weak_ptr指针,内部实现具体在weak_ptr讲解里。
在这里插入图片描述

  • 避免循环引用,如果A中有B的shared_ptr,B中有A的shared_ptr,那同时AB的两个对象在同一作用域在离开作用域时,a和b的引用计数都不会-1导致两个对象都不被析构,解决方法是把一个shared_ptr改成weak_ptr

unique_ptr独占的智能指针

unique_ptr是一个独占型智能指针,不能共享,不允许直接赋值给另一个unique_ptr

使用方式

  • 初始化:只能使用构造初始化C++11没有reset方法初始化,C++14会提供
	unique_ptr<int> p(new(int));
	unique_ptr<int> p2 = p; // 不允许直接赋值
  • 指针转移:转移内存所有权
    unique_ptr<int> p(new(int));
    *p = 1;
    std::cout <<"p:  "<< p << "  *p:  " << *p << std::endl;
    unique_ptr<int> p3 = std::move(p);
    std::cout << "p:  " << p /*<< *p */<< std::endl;    // 错误p == nullptr *p崩溃
    std::cout << "p3: " << p3 << "  *p3: " << *p3 << std::endl;

转移所有权以后原来的指针P已经是nullptr了,输出结果:
在这里插入图片描述

  • 指定删除器:跟shared_ptr不同的是需要确定删除器类型
	void Delete(int* p)
	{
		delete p;
	}
	std::unique_ptr<int, void(*)(int*)> p(new int, Delete);  //void(*)(int*) 指定删除器为函数指针
	
	// 捕获变量的lambda表达式不能转换成函数指针,但可以转换为std::function
	std::shared_ptr<int, std::function<void(int*)>> p(new int, [&](int* p){ deletw p;});

weak_ptr

弱指针weak_ptr用来监视shared_ptr的,不会使shared_ptr的引用计数增加,不会管理shared_ptr内部的指针,主要使为了监视shared_ptr的生命周期,不能共享资源,不能操作资源,构造不会增加引用计数,析构不会减少引用计数,可以用来返回this指针还有解决循环引用计数问题。

基本用法

  • use_count()获取当前观测资源的引用计数
	std::shared_ptr<int> p(new int(1));
	std::shared_ptr<int> wp(p);
	std::cout << wp.use_count() << std::endl; // 输出1
  • expried() 判断当前资源是否已经释放
	if(wp.expried())
		std::cout <<"无效"<<std::endl;
	else
		std::cout <<"有效"<<std::endl;
  • 通过lock()方法获取所监视的shared_ptr
std::weak_ptr<int> pw;
void f()
{
    if (pw.expired())  //所监视的shared_ptr是否被释放
        cout << "shared_ptr已释放 " << std::endl;
    else
        cout << "*shared_ptr = " << *pw.lock() << std::endl;
}
int main()
{
    {
        std::shared_ptr<int> sp = std::make_shared<int>(1);
        pw = sp;  // pw指针监视共享指针sp
        f();      // sp未被释放
    } // sp的作用域,sp离开这个作用域之后会被释放

    f();// 新作用域sp已经被释放
}
  • 使用weak_ptr返回this指针

上面将shared_ptr的时候讲了如何返回this指针:正确返回this指针的做法是,让目标类通过派生std::enable_shared_from_this类,使用成员函数shared_from_this返回this指针。

std::enable_shared_from_this 的内部可以看到里面有一个weak_ptr :_Wptr,当要获取类的shared_ptr的时候通过获取weak_ptr监视的shared_ptr获取的

template <class _Ty>
class enable_shared_from_this { 
public:
	shared_ptr<_Ty> shared_from_this() {
        return shared_ptr<_Ty>(_Wptr);
    }
    shared_ptr<const _Ty> shared_from_this() const {
        return shared_ptr<const _Ty>(_Wptr);
    }
    weak_ptr<_Ty> weak_from_this() noexcept {
        return _Wptr;
    }
    weak_ptr<const _Ty> weak_from_this() const noexcept {
        return _Wptr;
    }
protected:
    constexpr enable_shared_from_this() noexcept : _Wptr() {}
    enable_shared_from_this(const enable_shared_from_this&) noexcept : _Wptr() {
    }
    enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept { 
        return *this;
    }
    ~enable_shared_from_this() = default;
private:
    friend class shared_ptr;
    mutable weak_ptr<_Ty> _Wptr;
};
  • 解决shared_ptr循环引用问题:将A或者B中的任意一个共享指针变成weak_ptr就可以了
struct A
{
	std::shared_ptr<B> bptr;
	~A()
	{
		std::cout << "~A" << std::endl;
	};
};
 
struct B
{
	std::weak_ptr<A> aptr;
	~B()
	{
		std::cout << "~B" << std::endl;
	};
};
int main()
{
	std::shared_ptr<A> ap(new A);
	std::shared_ptr<B> bp(new B);
	ap->bptr = bp;
	bp->aptr = ap;
}

智能指针的使用选择

如果希望只有一个指针管理一个资源或者管理一个数组就用unique_ptr,如果希望多个指针管理使用同一个资源就用shared_ptr,weak_ptr用来帮助解决shared_ptr引发的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值