一、产生的原因
shared_ptr的产生与unique_ptr类似,都是为了解决raw pointer的new和delete的成对使用,导致的野指针、内存泄漏、重复释放内存等。
不过shared_ptr与unique_ptr场景又有所不同,这里主要是一个raw pointer在不同的代码块之间传来传去的场景,或者指针指向的内存比较大,这段内存可以切分成很多小部分,但是他们却需要共享彼此的数据。参考官方文档:
std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object.
https://en.cppreference.com/w/cpp/memory/shared_ptr
二、特性
shared_ptr 有两个特性:
特性1: 对raw pointer进行了一层封装,让C++程序员不用在担心何时去释放分配好的内存。
特性2: 共享,使用shared_ptr的指针可以共享同一块内存中的数据。
思想是:该类型智能指针在实现上采用的是引用计数机制,即便有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放)。
参见官方文档:
The object is destroyed and its memory deallocated when either of the following happens:
1.the last remaining shared_ptr owning the object is destroyed;2.the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().
https://en.cppreference.com/w/cpp/memory/shared_ptr
三、常用操作的示例
1. 构造函数:
#include <memory>
#include <iostream>
struct Foo {
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;
}
};
int main()
{
{
std::cout << "constructor with no managed object\n";
std::shared_ptr<Foo> sh1;
bool ok = sh1.get()==nullptr;
std::cout<<ok<<'\n';
}
// copy构造函数的话,引用计数都会增加
{
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';
}
// 可以指定删除的函数,并传递给构造函数
{
std::cout << "constructor with object and deleter\n";
std::shared_ptr<Foo> sh4(new Foo, D());
std::shared_ptr<Foo> sh5(new Foo, [](auto p) {
std::cout << "Call delete from lambda...\n";
delete p;
});
}
}
输出:
constructor with no managed object
1 // shared_ptr 默认构造函数分配的是空指针
constructor with object
Foo...
2 // sh2 和sh3指向的都是同一个内存,所以他们的引用计数都是2
2
~Foo...
constructor with object and deleter
Foo...
Foo...
Call delete from lambda...
~Foo...
Call delete from function object...
~Foo..
2.reset :
#include <memory>
#include <iostream>
struct Foo {
Foo(int n = 0) noexcept : bar(n) {
std::cout << "Foo: constructor, bar = " << bar << '\n';
}
~Foo() {
std::cout << "Foo: destructor, bar = " << bar << '\n';
}
int getBar() const noexcept { return bar; }
private:
int bar;
};
int main()
{
std::shared_ptr<Foo> sptr = std::make_shared<Foo>(1);
std::cout << "The first Foo's bar is " << sptr->getBar() << "\n";
std::cout<<"refer_count:"<<sptr.use_count()<<std::endl;
// reset the shared_ptr, hand it a fresh instance of Foo
// (the old instance will be destroyed after this call)
sptr.reset(new Foo);
std::cout << "The second Foo's bar is " << sptr->getBar() << "\n";
std::cout<<"refer_count:"<<sptr.use_count()<<std::endl;
}
输出:
Foo: constructor, bar = 1 // 初始化第一个指针
The first Foo's bar is 1
refer_count:1 // 这里会把引用计数设置为1
Foo: constructor, bar = 0 // 初始化第二个指针
Foo: destructor, bar = 1 // 析构掉第1个指针
The second Foo's bar is 0 // 验证第二个指针的变量是0
refer_count:1 // 第一个释放了,第二个新建了,引用计数还是1
Foo: destructor, bar = 0 // 退出main函数析构掉
3. make_shared
// make_shared example
#include <iostream>
#include <memory>
int main () {
std::shared_ptr<int> foo = std::make_shared<int> (10);
// same as: make_shared是推荐的用法,因为它会一次性将raw pointer和引用计数的内存同时分配好
std::shared_ptr<int> foo2 (new int(10));
auto bar = std::make_shared<int> (20);
auto baz = std::make_shared<std::pair<int,int>> (30,40);
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';
return 0;
}
输出:
*foo: 10
*bar: 20
*baz: 30 40
4.allocate_shared
// allocate_shared example
#include <iostream>
#include <memory>
int main () {
std::allocator<int> alloc; // the default allocator for int
std::default_delete<int> del; // the default deleter for int
std::shared_ptr<int> foo = std::allocate_shared<int> (alloc,10);
auto bar = std::allocate_shared<int> (alloc,20);
auto baz = std::allocate_shared<std::pair<int,int>> (alloc,30,40);
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';
return 0;
}
输出:
*foo: 10
*bar: 20
*baz: 30 40
四、主要函数实现
shared_ptr的代码声明如下所示:
// file: memory
template<class _Tp>>
class shared_ptr {
public:
typedef _Tp element_type;
private:
element_type* __ptr_; // raw pointer的指针
__shared_weak_count* __cntrl_; // 引用计数的实现也是指针
struct __nat{int __for_bool_;}; // placeholder
// ...
};
// 如果 Yp* 能够转换成 _Tp*,则可以由 _Yp* 构造一个shared_ptr<_Tp>
template<class _Tp>
template<class _Yp>
shared_ptr<_Tp>::shared_ptr(_Yp* __p,
typename enable_if<is_convertible<_Yp*, element_type*>::value, __nat>::type)
: __ptr_(__p) {
unique_ptr<_Yp> __hold(__p);
typedef __shared_ptr_pointer<_Yp*, default_delete<_Yp>, allocator<_Yp> >_CntrBlk;
__cntrl_ = new _CntrBlk(__p, default_delete<_Yp>(), allocator<_Yp>());
__hold.release();
}
// copy constructor, increment reference count
template<class _Tp>
inline shared_ptr<Tp>::shared_ptr(const shared_ptr& __r) noexcept
: __ptr(__r.__ptr_), __cntrl_(__r.__cntrl_) {
if (__cntrl_)
__cntrl_->__add_shared();
}
// move constructor, does't increment reference count
template<class _Tp>
inline shared_ptr<T>::shared_ptr(shared_ptr&& __r) noexcept
: __ptr_(__r.__ptr_), __cntrl_(__r.__cntrl) {
__r.__ptr_ = 0;
__r.__cntrl_ = 0;
}
template<class _Tp>
shared_ptr<_Tp>::~shared_ptr(){
if (__cntrl_) // 根据引用计数的数量来进行不同的操作
__cntrl_->__release_shared();
}
涉及到的引用计数类的声明
class __shared_count { // 引用计数的操作类
// not copy constructible and not assignable
__shared_count(const __shared_count&);
__shared_count& operator=(const __shared_count&);
protected:
long __shared_owners_; // how many owners do I have?
virtual ~__shared_count();
public:
explicit __shared_count(long __refs = 0) noexcept
: __shared_owners(__refs){}
void __add_shared() noexcept;
bool __release_shared() noexcept;
};
class __shared_weak_count : private __shared_count {
long __shared_weak_owners_;
public:
explicit __shared_weak_count(long __refs = 0) noexcept {
: __shared_count(__refs),
__shared_weak_owners(__refs) {}
protected:
virtual ~__shared_weak_count();
public:
void __add_shared() noexcept;
void __add_weak() noexcept;
void __release_shared() noexcept;
void __release_weak() noexcept;
long use_count() const noexcept { return __shared_count::use_count();}
private:
virtual void __on_zero_shared_weak() noexcept = 0;
};
补充资料:
http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/
https://en.cppreference.com/w/cpp/memory/shared_ptr
http://c.biancheng.net/view/7898.html
https://blog.csdn.net/thinkerleo1997/article/details/78754919
https://blog.csdn.net/shaosunrise/article/details/85228823
https://www.jianshu.com/p/d365bfbb83a3