C++系列之智能指针(smart pointer)
c11开始,standary library 支持两大类型的smart pointer:
1.shared_ptr:共享式指针,多个smart_ptr可以指向同一对象,该对象与其相关资源会在最后一个reference被销毁时释放。同时提供了weak_ptr,bad_weak_ptr,enable_shared_from_this等辅助类;
2.unique_ptr:独占式指针,保证一个对象同一时间只有一个smart_ptr指向该对象。可以转移该对象的拥有权,有效的避免了指针内存泄漏的问题。
shared_ptr的用法:
1.constructor
(1) constexpr shared_ptr() noexcept; //默认构造函数
(2) constexpr shared_ptr(nullptr_t) : shared_ptr() {} //空指针
(3) template <class U> ;explicit shared_ptr (U* p); //显式U*构造
(4) template <class U, class D> shared_ptr (U* p, D del);
template <class D> shared_ptr (nullptr_t p, D del); //带有delete的
(5) template <class U, class D, class Alloc> shared_ptr (U* p, D del, Alloc alloc);
template <class D, class Alloc> shared_ptr (nullptr_t p, D del, Alloc alloc) ; //带有分配器
(6) shared_ptr (const shared_ptr& x) noexcept;
template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;//拷贝构造函数
(7) template <class U> explicit shared_ptr (const weak_ptr<U>& x); //拷贝weak_ptr
(8) shared_ptr (shared_ptr&& x) noexcept;
template <class U> shared_ptr (shared_ptr<U>&& x) noexcept; //move构造函数
(9) template <class U> shared_ptr (auto_ptr<U>&& x);
template <class U, class D> shared_ptr (unique_ptr<U,D>&& x); //其他
(10) template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;
成元函数:
1.shared_ptr::get();返回普通指针
2.shared_ptr::operator bool:是null->false,other->true
3.shared_ptr::operator *: 返回指针对象
4.shared_ptr::operator->:返回指针对象的成员
5.shared_ptr::operator=:支持拷贝赋值,和move赋值
**6. shared_ptr::owener_before:**
7. shared_ptr::reset: 给sptr重新赋值一个对象的指针
8. shared_ptr::swap:交换两个sptr,不改变各自的引用计数
**9. shared_ptr::unique:判断该对象的sptr是否是唯一的**
**10.shared_ptr::count: 同一对象的sptr的计数**
代码演示
# include<iostream>
# include<memory>
# include<string>
using namespace std;
int main()
{
//构造函数
string s1 = "string3";
shared_ptr<string> sptr1(new string("string1"));
shared_ptr<string> sptr2(sptr1);
shared_ptr<string> sptr3(&s1);
shared_ptr<string> sptr4;
shared_ptr<string> sptr5(unique_ptr<string>(new string("string5")));
// 成员函数
sptr3.get(); // return &s1;
if(sptr1) cout <<"sptr1 is true " <<endl; //cout ture;自动转为bool值
*sptr1 ; //string1;
sptr1.swap(sptr2); // swap
spat1.use_count(); // 指针计数
}
unique_ptr的使用
1.construct
1.constexp unique_ptr() noexcept; //默认
2.constexp unique_ptr() noexcept : unique_ptr(){};
3.explict unique_ptr( pointer p) noexcept;
4.unique_ptr(pointer p, typeneme conditon<is_reference<D>::value,D,const D& > del) noexcept;
5.unique_ptr(pointer p, typename remove_reference<D>::type&& del) noexcept;
6.unique_ptr(uniqe_ptr && x) noexcept; // move
7.template<class U, class E> unique_ptr(unique_ptr<U,E>&& x) noexcept; //move
8.unique_ptr(const unique_ptr&) = delete; //废掉了copy
成员函数
1.unique_ptr::get
**2.unique_ptr::get_deleter**
3.unique_ptr::operator bool
4.unique_ptr::operator *
5.unique_ptr::operator ->
6.uniqeu_ptr::operator =
**7.uniqeu_ptr::operator []**
**8.unique_ptr::release**
9.unique_ptr::reset
10.unique_ptr::swap
#include <iostream>
#include <memory>
int main ()
{
std::default_delete<int> d;
std::unique_ptr<int> u1;
std::unique_ptr<int> u2 (nullptr);
std::unique_ptr<int> u3 (new int);
std::unique_ptr<int> u4 (new int, d);
std::unique_ptr<int> u5 (new int, std::default_delete<int>());
std::unique_ptr<int> u6 (std::move(u5));
std::unique_ptr<int> u7 (std::move(u6));
std::unique_ptr<int> u8 (std::auto_ptr<int>(new int));
}
weak_ptr
允许”共享但不拥有“某对象,这个class会建立起一个shared pointer。 一旦最后一个拥有该对象的shared pointer失去了拥有权,任何weak pointer都将会自动变空。因此:在default和copy构造函数外,只提供接收一个shared ptr的构造函数.
成员函数
1. weak_ptr::expired //判断shared_ptr是否已经不存在
2. weak_ptr::lock //可以使用该shared_ptr的*,->操作符
4. weak_ptr::operator= //赋值
5. weak_ptr::owner_before
6. weak_ptr::reset //置空,null
7. weak_ptr::swap //交换
8. weak_ptr::use_count //计数
注意事项
1.避免##循环依赖##出现,从而造成”空荡指针“
class A;
class B
{
shared_ptr<A> spt1;
}
class A
{
shared_ptr<B> spt1;
}
int main
{
A a;
B b;
a.spt1 = &b;
b.spt2 = &a;
//将会导致a,b无法正常析构,因为其计数永远大于0;
}
2.确保对象只被一组shared pointer使用;
int *p = new int(4);
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);
//会导致sp1,sp2都会在丢失p的拥有权时释放相应的资源(调用delete).
//正确做法如下:
int *p = new int(4);
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(sp1); //ok
3.deleter的使用,一旦拥有权被转移到一个已拥有过其他对象的shared pointer时候,dleter就会被先前拥有的那个对象所调用(当shared pointer 是最后一个拥有者的时候)。
4.alias 构造函数,
//Alia构造函数,建立一个shared pointer,共享sp2的拥有权,但是指向ptr
shared_ptr<T> sp (sp2, ptr)
//example
class A {int a;};
shared_ptr<A> ptr1(new A);
shared_ptr<int> ptr2(ptr1,&(ptr1->a)); //ptr2指向(ptr1->a)
// 必须确保两对象(A,a)的寿命一致,否则会产生“空荡指针”,或者“资源泄露”;
5.线程安全
当并发访问的是pointer而非其指向的值时
std::shared_ptr<X> global; //初始化空指针
void foo()
{
std::shared_ptr<X> local{new X};
...
std::atomic_store(&global,local);
6 unique_ptr作为类成员
class A
{
public:
A() = default;
~A() = delete; //不需要了,牛牛牛
private:
unique_ptr<int> ptr1 = new int(5);
unique_ptr<int[]> ptr1 = new int[5]; //因为delete无法知道删除的是对象还是数组,所以又有一个unique_ptr<T[]>的偏特化版本,重载的operator [],不再提供*和->运算(&p[i]不香吗
结束语
由于smart pointer带来方便的同时也带来了性能损失,这也是为什么c++标准库不是只提供smart pointer的原因。shared_pointer需要一个额外的计数器,weak_ptr同上;unique_ptr可以媲美naive_ptr(其smart体现在特殊的**构造函数**和**析构函数**,如果给定的deleter是stateless或者empty,就不会有额外的内存开销),推荐使用function object(包括lamda)作为deleter,从而实现零开销。