shared_ptr属于C++11特性中新加的一种智能指针,它的实现方式是模版类,头文件
template
所以使用shared_ptr的声明方式即为
std::shared_ptr
其中type_id为类型(可以是基本数据类型或者类),statement即为模版类对象
shared_ptr 的理解如下:
常用操作以及源码实现如下:
类声明如下
temple<typename T>
class SharedPtr {
public:...
private:T *_ptr;int *_refCount; //这里使用int型指针是为了保证拷贝构造时同一个地址空间的引用计数增加
};
constructor
构造函数,初始化的时候默认引用计数为0SharedPtr() : _ptr((T *)0), _refCount(0)
{ }
在使用普通指针初始化两个shared_ptr的时候,两个shared_ptr的引用计数都为1SharedPtr(T *obj) : _ptr(obj), _refCount(new int(1))
{
}
在进行拷贝构造的时候,使用shared_ptr去初始化另一个shared_ptr的时候引用计数会+1SharedPtr(SharedPtr &other) : _ptr(other._ptr), _refCount(&(++*other._refCount))
{
}
destructor
析构函数,析构的时候在指针不为空且引用计数为0的时候释放空间~SharedPtr()
{ if (_ptr && --*_refCount == 0) { delete _ptr;delete _refCount;}
}
operator =
当使用一个shared_ptr给另一个shared_ptr赋值的时候这里需要注意 SharedPtr &operator=(SharedPtr &other)
{ if(this==&other)return *this;//新指针引用计数要++ ++*other._refCount;//原指针引用计数要--,如果为0,则释放空间if (--*_refCount == 0) { delete _ptr;delete _refCount;}//重新进行指向 _ptr = other._ptr;_refCount = other._refCount;return *this;
}
operator*
解引用运算符,直接返回底层指针的引用,即共享的地址空间内容T &operator*()
{ if (_refCount == 0)return (T*)0;return *_ptr;
}
operator ->
指针运算符T *operator->()
{ if(_refCount == 0)return 0;return _ptr;
}
主要案例如下
constructor
,std::shared_ptr初始化案例如下,以及对应的refcount打印#include
#include struct C { int* data;};int main () { std::shared_ptr<int> p1;//默认构造函数,refcount为0std::shared_ptr<int> p2 (nullptr);//使用一个空的对象初始化时refcount也为0//普通指针初始化是引用计数为1,p3,p4std::shared_ptr<int> p3 (new int);std::shared_ptr<int> p4 (new int, std::default_delete<int>());//拥有allocator的时候初始化同样引用计数为1//但是紧接着又用改智能指针拷贝构造初始化其他智能指针p6,//所以最后其引用计数为2,p5std::shared_ptr<int> p5 (new int, [](int* p){ delete p;}, std::allocator<int>());//这里p6本身为1,但是因为使用std::move去初始化p7,将p6指向转给了p7//则p6智能指针recount--变为0,p7 ++由1变为2std::shared_ptr<int> p6 (p5);std::shared_ptr<int> p7 (std::move(p6));std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));std::shared_ptr<C> obj (new C);std::shared_ptr<int> p9 (obj, obj->data);std::cout << "use_count:
";std::cout << "p1: " << p1.use_count() << '
';std::cout << "p2: " << p2.use_count() << '
';std::cout << "p3: " << p3.use_count() << '
';std::cout << "p4: " << p4.use_count() << '
';std::cout << "p5: " << p5.use_count() << '
';std::cout << "p6: " << p6.use_count() << '
';std::cout << "p7: " << p7.use_count() << '
';std::cout << "p8: " << p8.use_count() << '
';std::cout << "p9: " << p9.use_count() << '
';return 0;
}
输出如下use_count:
p1: 0
p2: 0
p3: 1
p4: 1
p5: 2
p6: 0
p7: 2
p8: 1
p9: 2
// shared_ptr destructor example
#include
#include int main () { auto deleter = [](int*p){ std::cout << "[deleter called]
"; delete p;};//使用特殊的delete函数去构造,析构的时候会执行改delete 中lamada表达式内容.即构造函数案例中的p5初始化方式std::shared_ptr<int> foo (new int,deleter);std::cout << "use_count: " << foo.use_count() << '
';return 0; // [deleter called]
}
输出如下use_count: 1
[deleter called]
// shared_ptr::operator= example
#include
#include int main () { std::shared_ptr<int> foo;std::shared_ptr<int> bar (new int(10));/*此时foo的引用计数为0,bar初始化后引用计数为1这里进行赋值操作,即foo的指向发生了变化,指向了bar1.foo引用计数--,因为已经为0了,此时直接释放foo原来的空间2.bar引用计数++变为23.更改foo的引用计数和bar引用计数相等,并使得foo指向bar.因为他们共享同一个空间执行完之后fool和bar引用计数都相等,且解引用后数值都为0*/foo = bar; // copystd::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '
';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '
';/*这里重新对bar进行了初始化,即原先的指向发生了更改,所以它的引用计数--,并且内容变为新的地址空间内容20foo继续指向原先空间,但是内容并未变化。同时原先地址因为bar并不引用了,所以foo的引用计数--*/bar = std::make_shared<int> (20); // movestd::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '
';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '
';std::unique_ptr<int> unique (new int(30));foo = std::move(unique); // move from unique_ptrstd::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '
';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '
';return 0;
}
输出如下*foo: 10 foo.count 2
*bar: 10 bar.count 2
*foo: 10 foo.count 1
*bar: 20 bar.count 1
*foo: 30 foo.count 1
*bar: 20 bar.count 1
// shared_ptr::swap example
#include
#include int main () { std::shared_ptr<int> foo (new int(10));std::shared_ptr<int> bar (new int(20));std::cout << "befor swap" << '
';std::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '
';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '
';foo.swap(bar);std::cout << "after swap" << '
';std::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '
';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '
';return 0;
}
输出如下befor swap
*foo: 10 foo.count 1
*bar: 20 bar.count 1
after swap
*foo: 20 foo.count 1
*bar: 10 bar.count 1
// shared_ptr::reset example
#include
#include int main () { std::shared_ptr<int> sp; // emptysp.reset (new int); // 替换所管理对象,让其更换地址指向std::cout << *sp << " " << sp.use_count() << " " << sp << '
';*sp=10;std::cout << *sp << " " << sp.use_count() << " " << sp << '
';sp.reset (new int); // 清除上一个指针指向的内容,重新进行更换std::cout << *sp << " " << sp.use_count() << " " << sp << '
';*sp=20;std::cout << *sp << " " << sp.use_count() << " " << sp << '
';sp.reset(); // deletes managed object//std::cout << *sp << " " << sp.use_count() << '
';return 0;
}
输出如下,可以看到reset之后的地址发生了变化,即更改了指针的指向0 1 0x434cd50
10 1 0x434cd50
0 1 0x434cd90
20 1 0x434cd90
// shared_ptr::get example
#include
#include int main () { int* p = new int (10);std::shared_ptr<int> a (p);std::shared_ptr<int> b (new int(20));//此时a和p共享同一个地址空间,所以a和p的内容都为0,地址空间一样if (a.get()==p)std::cout << "a and p point to the same location " << a << " " << p << '
';std::cout << *a.get() << "
";std::cout << *a << "
";std::cout << *p << "
";//此时a将共享空间释放,重新更换指向b,但是*p为普通指针,并无法跟随a更换指,所以p的地址内容变为0a=b;std::cout << "a and p after copy " << a << " " << b << '
';// three ways of accessing the same address:std::cout << *a.get() << "
";std::cout << *a << "
";std::cout << *p << "
";return 0;
}
输出如下:a and p point to the same location 0x21cf2f0 0x21cf2f0
10
10
10
a and p after copy 0x21cf330 0x21cf330
20
20
0
循环引用是指两个shared_ptr初始化之后相互指向,在函数作用域结束之后由于两个指针都保持相互的指向,引用计数都为1,此时各自占用的内存空间无法释放,最终产生内存泄露
举例如下:
#include
#include using namespace std; class B;
class A{ public: shared_ptr<B> ptr_A; ~A(){ cout << "refcount " << ptr_A.use_count() << '
';cout<<"~A()"<<endl; }
};
class B{ public: //shared_ptr ptr_B;//当采用shared_ptr指向A时会形成循环引用,则什么都不会输出说明对象没有被析构,可怕的内存泄露.... shared_ptr<A> ptr_B;//当采用弱引用时,避免了循环引用,有输出,说明对象被析构了 ~B(){ cout << "refcount " << ptr_B.use_count() << '
';cout<<"~B()"<<endl; }
};
int main(){ shared_ptr<A> a(new A); shared_ptr<B> b(new B); a->ptr_A=b; b->ptr_B=a;//若是循环引用:当a、b退出作用域的时候,A对象计数不为1(b保留了个计数呢),同理B的计数也不为1,那么对象将不会被销毁,内存泄露了... cout << a.use_count() << " " << b.use_count()<< endl;return 0;
}
输出如下,可以看到释放的之前两个智能指针的引用计数都为2,析构的时候各自引用计数执行–到·1,最终无法释放
2 2
将classB中的shared_ptr更改为weak_ptr即可成功释放
#include
#include using namespace std; class B;
class A{ public: shared_ptr<B> ptr_A; ~A(){ cout << "refcount " << ptr_A.use_count() << '
';cout<<"~A()"<<endl; }
};
class B{ public: //shared_ptr ptr_B;//当采用shared_ptr指向A时会形成循环引用,则什么都不会输出说明对象没有被析构,可怕的内存泄露.... weak_ptr<A> ptr_B;//当采用弱引用时,避免了循环引用,有输出,说明对象被析构了 ~B(){ cout << "refcount " << ptr_B.use_count() << '
';cout<<"~B()"<<endl; }
};
int main(){ shared_ptr<A> a(new A); shared_ptr<B> b(new B); a->ptr_A=b; b->ptr_B=a;//若是循环引用:当a、b退出作用域的时候,A对象计数不为1(b保留了个计数呢),同理B的计数也不为1,那么对象将不会被销毁,内存泄露了... return 0;
}
输出如下,调用析构函数之前引一个智能指针的引用计已经将为1,执行析构之后即为0
1 2
refcount 1
~A()
refcount 0
~B()
参考文档:
http://www.cplusplus.com/reference/memory/shared_ptr/
https://www.xuebuyuan.com/3190713.html
C++11标准中可以为模板定义别名,比如
template
先说结论,智能指针都是非线程安全的。
多线程调度智能指针
这里案例使用的是shared_ptr,其他的unique_ptr或者weak_ptr的结果都是类似的,如下多线程调度代码:
#include
文章目录unique_ptr 智能指针的实现shared_ptr 智能指针的实现指针类型转换
unique_ptr 智能指针的实现
一个对象只能被单个unique_ptr 所拥有。
#include
文章目录weak_ptr描述声明作用原理实现函数成员使用总结
weak_ptr描述
声明
头文件:
enable_shared_from_this解析 enable_shared_from_this,是一个以其派生类为模板类型实参的基础模板,继承它,this指针就能变成shared_ptr。 什么时候该使用enable_shared_from_this模板类 在看下面的例子之前,简单说下使用背景,单有...
经过长期探索,发现一个不需要手动设置线程休眠时间(e.g. std::this_thread::sleep_for(std::chrono::microseconds(1)))的代码: Github: https://github.com/log4cplus/ThreadPool #ifndef THREAD_POOL_H_7e...
nth_element(first,nth,last) first,last 第一个和最后一个迭代器,也可以直接用数组的位置。 nth,要定位的第nn 个元素,能对它进行随机访问. 将第n_thn_th 元素放到它该放的位置上,左边元素都小于它,右边元素都大于它. 测试代码: http://www.cplusplus.com...
c/c++老版本的rand()存在一定的问题,在转换rand随机数的范围,类型或者分布时,常常会引入非随机性。
定义在
jsoncpp 是一个C++ 语言实现的json库,非常方便得支持C++得各种数据类型到json 以及 json到各种数据类型的转化。 一个json 类型的数据如下: {"code" : 10.01,"files" : "","msg" : "","uploadid" : "UP000000" } 这种数据类型方便我们人阅读以...
问题如下: 已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子 集中的各个元素和为整数target的子集,结果中无重复的子集。 例如: nums[] = [10, 1, 2, 7, 6, 1, 5], target = 8 结果为: [[1, 7], [1, 2, 5], [2, 6], [1, 1, 6]] 同样之前有...