C++ 标准库读书杂记7 Smart Pointer

C++没有内存回收机制,每次程序员new出来的对象需要手动delete,流程复杂时可能会漏掉delete,导致内存泄漏。于是C++引入智能指针,可用于动态资源管理,资源即对象的管理策略。

使用 raw pointer 管理动态内存时,经常会遇到这样的问题:

  • 忘记delete内存,造成内存泄露。
  • 出现异常时,不会执行delete,造成内存泄露。

智能指针主要有三种:shared_ptr,unique_ptr和weak_ptr。

 shared_ptr

shared_ptr是最常用的智能指针(项目中我只用过shared_ptr)。shared_ptr采用了引用计数器,多个shared_ptr中的T *ptr指向同一个内存区域(同一个对象),并共同维护同一个引用计数器。shared_ptr定义如下,记录同一个实例被引用的次数,当引用次数大于0时可用,等于0时释放内存。

注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。循环引用在weak_ptr中介绍。引用传递不加1;

shared_ptr对象每次离开作用域时会自动调用析构函数,而析构函数并不像其他类的析构函数一样,而是在释放内存是先判断引用计数器是否为0。等于0才做delete操作,否则只对引用计数器左减一操作。

接下来看一下构造函数,默认构造函数的引用计数器为0,ptr指向NULL:

SharedPtr() : _ptr((T *)0), _refCount(0)
{
}

~SharedPtr()
{
    if (_ptr && --*_refCount == 0) {
        delete _ptr;
        delete _refCount;
    }
}

用普通指针初始化智能指针时,引用计数器初始化为1:

SharedPtr(T *obj) : _ptr(obj), _refCount(new int(1))
{
}

赋值运算符的重载

当用一个shared_ptr<T> other去给另一个 shared_ptr<T> sp赋值时,发生了两件事情:

一、sp指针指向发生变化,不再指向之前的内存区域,所以赋值前原来的_refCount要自减

二、sp指针指向other.ptr,所以other的引用计数器_refCount要做++操作。

template <class T> class shared_ptr;
Member functions
(constructor)
Construct shared_ptr (public member function )
(destructor)
Destroy shared_ptr (public member function )
operator=
shared_ptr assignment (public member function )
swap
Swap content (public member function )
reset
Reset pointer (public member function )
get
Get pointer (public member function )
operator*
Dereference object (public member function )
operator->
Dereference object member (public member function )
use_count
Use count (public member function )
unique
Check if unique (public member function )
operator bool
Check if not null (public member function )
owner_before
Owner-based ordering (public member function template )

Non-member functions
Overloads:
swap
Exchange content of shared_ptr objects (function template )
relational operators
Relational operators ==, !=, <, <=, >, >= (function template )
ostream operator<<
Insert into output stream (function template )

Specific functions:
make_shared
Make shared_ptr (function template )
allocate_shared
Allocate shared_ptr (function template )
static_pointer_cast
Static cast of shared_ptr (function template )
dynamic_pointer_cast
Dynamic cast of shared_ptr (function template )
const_pointer_cast
Const cast of shared_ptr (function template )
get_deleter
Get deleter from shared_ptr (function template )


See also
weak_ptr
Weak shared pointer (class template )
owner_less
Owner-based less-than operation (class template )

实例:

#include "shared_ptr.h"

//class Foo {
//public:
//	Foo() { std::cout << "Foo...\n"; }
//	~Foo() { std::cout << "~Foo...\n"; }
//};
//
//struct D {
//	void operator()(Foo* p) const {
//		std::cout << "Call delete from function object...\n";
//		delete p;
//	}
//};

void func_shared_ptr(const shared_ptr<int>  foo_)
{
	cout << "foo_.use_count =  " << foo_.use_count() << endl;
}


int stl_shared_ptr()
{
	std::shared_ptr<int> p1;
	{
		std::cout << "use_count:\n";
		std::cout << "p1: " << p1.use_count() << '\n';
		p1.reset(new int(0));
		std::cout << "p1: " << p1.use_count() << '\n';
		std::shared_ptr<int> p2(nullptr);
		std::cout << "p2: " << p2.use_count() << '\n';
		std::shared_ptr<int> p3(new int);
		std::cout << "p3: " << p3.use_count() << '\n';
		std::shared_ptr<int> p4(new int, std::default_delete<int>());//给出析构函数
		std::cout << "p4: " << p4.use_count() << '\n';
		std::shared_ptr<int> p5(new int, [](int* p){delete p; }, std::allocator<int>());//给出构造函数和析构函数
		std::cout << "p5: " << p5.use_count() << '\n';
		{
			std::shared_ptr<int> p6(p5);//调用拷贝构造函数 p6原本的减一,p5加一
			std::cout << "p5in: " << p5.use_count() << '\n';
			std::cout << "p6in: " << p6.use_count() << '\n';
		}
		std::cout << "p5: " << p5.use_count() << '\n';
		std::shared_ptr<int> p6(p5);
		std::shared_ptr<int> p7(std::move(p6));//左值变右值,p6失去对指针的权力
		std::cout << "p5: " << p5.use_count() << '\n';
		std::cout << "p6: " << p6.use_count() << '\n';
		std::cout << "p7: " << p7.use_count() << '\n';
		std::shared_ptr<int> p8(std::unique_ptr<int>(new int));
		std::cout << "p8: " << p8.use_count() << '\n';
		std::shared_ptr<C> obj(new C);
		std::shared_ptr<int> p9(obj, obj->data);//构造
		std::cout << "p9: " << p9.use_count() << '\n';
	}
	
	std::cout << "p3: " << p1.use_count() << '\n';


	//考察一下引用传递对共享次数 指针的传递就会增加引用计数。

	{
		auto foo_s_ptr = make_shared<int>(12);// 注意maked的使用
		shared_ptr<int> foo_s_ptr2(foo_s_ptr);
		cout << "foo_s_ptr.use_count =  " << foo_s_ptr.use_count() << endl;
		cout << "foo_s_ptr2.use_count =  " << foo_s_ptr2.use_count() << endl;
		shared_ptr<int> foo_s_ptr3(foo_s_ptr);
		cout << "foo_s_ptr.use_count =  " << foo_s_ptr.use_count() << endl;

		func_shared_ptr(foo_s_ptr2);
		cout << "foo_s_ptr.use_count =  " << foo_s_ptr.use_count() << endl;
	}

	//类型
	{
		std::cout << "constructor with no managed object\n";
		std::shared_ptr<Foo> sh1;
	}
	//调用构造函数
	{
	std::cout << "constructor with object\n";
	std::shared_ptr<Foo> sh2(new Foo);
	std::shared_ptr<Foo> sh3(sh2);
	std::cout << sh2.use_count() << '\n';
	std::cout << sh3.use_count() << '\n';
}
	//注册deleter
	{
		std::cout << "constructor with object and deleter\n";
		std::shared_ptr<Foo> sh4(new Foo, D());
		std::shared_ptr<Foo> sh5(new Foo, [](Foo *p) {
			std::cout << "Call delete from lambda...\n";
			delete p;
		});
	}
	return 1;
}

Class unique_ptr 

unique_ptr 不共享它的指针。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法。只能移动unique_ptr。这意味着,内存资源所有权将转移到另一 unique_ptr,并且原始 unique_ptr 不再拥有此资源。我们建议你将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于纯 C++ 对象时,可使用 unique_ptr,而当构造 unique_ptr 时,可使用make_unique Helper 函数。

拷贝一个std::unique_ptr将不被允许,因为如果你拷贝一个std::unique_ptr,那么拷贝结束后,这两个std::unique_ptr都会指向相同的资源,它们都认为自己拥有这块资源(所以都会企图释放)。

因此std::unique_ptr是一个仅能移动(move_only)的类型。

1.创建

unique_ptr不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。要想创建一个unique_ptr,我们需要将一个new 操作符返回的指针传递给unique_ptr的构造函数。

int main()
{
    // 创建一个unique_ptr实例
    unique_ptr<int> pInt(new int(5));
    cout << *pInt;
}

2、无法进行复制构造和赋值操作

unique_ptr没有copy构造函数,不支持普通的拷贝和赋值操作。

int main() 
{
    // 创建一个unique_ptr实例
    unique_ptr<int> pInt(new int(5));
    unique_ptr<int> pInt2(pInt);    // 报错
    unique_ptr<int> pInt3 = pInt;   // 报错
}

3、可以进行移动构造和移动赋值操作

unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。如果需要转移所有权,可以使用std::move()函数。

int main() 
{
    unique_ptr<int> pInt(new int(5));
    unique_ptr<int> pInt2 = std::move(pInt);    // 转移所有权
    //cout << *pInt << endl; // 出错,pInt为空
    cout << *pInt2 << endl;
    unique_ptr<int> pInt3(std::move(pInt2));
}

4、可以返回unique_ptr

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

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

int main() {
    int p = 5;
    unique_ptr<int> ret = clone(p);
    cout << *ret << endl;
}

5.unique_ptr使用场景

为动态申请的资源提供异常安全保证

返回函数内动态申请资源的所有权

在容器中保存指针

管理动态数组 

作为auto_ptr的替代品

实例 

#include "unique_ptr.h"
#include <iostream>
#include <functional>
using namespace std;

void f(const unique_Foo &)
{
	std::cout << "f(const unique_Foo&)\n";
}
void TestAutoDestroy()
{
	//1. 普通的new对象.
	std::cout << "TestDestroy...................." << std::endl;
	{
		std::unique_ptr<unique_Foo> p1(new unique_Foo);
	}
	//2. 普通的new[]对象.
	{
		std::unique_ptr<unique_Foo[]> p2(new unique_Foo[4]);//unique_ptr数组模式
	}
	//3. 自定义的deleter.和shared_ptr类似可以自己写一个析构函数,有多种方式
	{
		std::unique_ptr<unique_Foo, unique_D> p3(new unique_Foo);
		
		std::unique_ptr<unique_Foo, std::function<void(unique_Foo*)>>  p4(new unique_Foo, [](unique_Foo *p){ delete p; });//类似的lambda 表达式
	}
}
void TestOwner()
{
	std::cout << "TestOwner...................." << std::endl;
	//1. new object.
	std::unique_ptr<unique_Foo> p1(new unique_Foo);  // p1 owns unique_Foo
	if (p1) p1->bar();

	{
		std::unique_ptr<unique_Foo> p2(std::move(p1));  // now p2 owns unique_Foo
		f(*p2);

		p1 = std::move(p2);  // ownership returns to p1
		p2->bar();
		std::cout << "destroying p2...\n";
	}

	p1->bar();
}

void TestArrayOwner()
{
	std::cout << "TestArrayOwner...................." << std::endl;
	//1. new[] object.
	std::unique_ptr<unique_Foo[]> p1(new unique_Foo[4]);  // p1 owns unique_Foo
	if (p1) p1[0].bar();

	{
		std::unique_ptr<unique_Foo[]> p2(std::move(p1));  // now p2 owns unique_Foo
		f(p2[0]);

		p1 = std::move(p2);  // ownership returns to p1
		p2[0].bar();
		std::cout << "destroying p2...\n";
	}

	p1[0].bar();
}

int stl_unique_ptr()
{
	TestAutoDestroy();
	TestOwner();
	TestArrayOwner();
	return 0;
}

weak_ptr 

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段. 
  weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少. 

std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 std::shared_ptr

std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

此外,std::weak_ptr 还可以用来避免 std::shared_ptr 的循环引用。

weak_ptr 在功能上类似于普通指针, 然而一个比较大的区别是, 弱引用能检测到所管理的对象是否已经被释放, 从而避免访问非法内存。

(构造函数)

构造新的weak_ptr 
(公开成员函数)

(析构函数)

销毁 weak_ptr 
(公开成员函数)

operator=

weak_ptr赋值 
(公开成员函数)

修改器

reset

释放被管理对象的所有权 
(公开成员函数)

swap

交换被管理对象 
(公开成员函数)

观察器

use_count

返回管理该对象的 shared_ptr 对象数量 
(公开成员函数)

expired

检查被引用的对象是否已删除 
(公开成员函数)

lock

创建管理被引用的对象的shared_ptr 
(公开成员函数)

owner_before

提供弱指针的基于拥有者顺序 
(公开成员函数)

非成员函数

std::swap(std::weak_ptr)

(C++11)

特化 std::swap 算法 
(函数模板)

辅助类

std::atomic<std::weak_ptr>

(C++20)

原子弱指针 
(类模板特化)

weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象. 注意, weak_ptr 在使用前需要检查合法性.

expired 用于检测所管理的对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false.
lock 用于获取所管理的对象的强引用(shared_ptr). 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个 shared_ptr, 其内部对象指向与 weak_ptr 相同.
use_count 返回与 shared_ptr 共享的对象的引用计数.
reset 将 weak_ptr 置空.
weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数.

创建及使用

#include "weak_ptr.h"
using namespace std;


class CB;
class CA;

class CA
{
public:
	CA(){}
	~CA(){  }

	void Register(const std::shared_ptr<CB>& sp)
	{
		m_spb = sp;
		cout << "Register_spib.use_count = " << sp.use_count() << endl;
	}

private:
	std::weak_ptr<CB> m_spb;//这样不会增加m_spb的引用计数 ,那么CA就能释放
};

class CB
{
public:
	CB(){};
	~CB(){  };

	void Register(const std::shared_ptr<CA>& sp)
	{
		m_spa = sp;
		cout << "Register_spia.use_count = " << sp.use_count() << endl;
	}

private:
	std::shared_ptr<CA> m_spa;//与上条注释的差别所在,无论什么情况下,B的智能指针的释放都得等m_spa的引用计数为1.
};

std::weak_ptr<int> gw;

void f()
{
	if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr,且会增加原来的shared_ptr的计数
		std::cout << *spt << "\n";
		cout << "spt.use_count = " << spt.use_count() << endl;
	}
	else {
		std::cout << "gw is expired\n";
	}
}

void weak_ptr_lock()
{
	{
		auto sp = std::make_shared<int>(42);
		cout << "sp.use_count = " << sp.use_count() << endl;
		gw = sp;

		f();
		cout << "sp.use_count = " << sp.use_count() << endl;//使用计数恢复到1
	}

	f();
}

void stl_weak_circle()
{
	std::shared_ptr<CA> spa(new CA);
	std::shared_ptr<CB> spb(new CB);
	std::weak_ptr<CB> weak_cb = spb;//并不改变计数
	std::weak_ptr<CB> weak_cb1(spb);//并不改变计数

	cout << "spb.use_count = " << spb.use_count() << endl;
	cout << "spa.use_count = " << spa.use_count() << endl;

	spb->Register(spa);
	spa->Register(spb);
	cout << "spb.use_count = " << spb.use_count() << endl;
	cout << "spa.use_count = " << spa.use_count() << endl;

}



void stl_weak_ptr()
{
	weak_ptr_lock();
	stl_weak_circle();
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值