刚学编程时,最常听到的一句话是不是“new 的内存用完要记得 delete,不然会造成内存泄漏”?然而事实上是:
a solution involving the phrase "just remember to" is seldom the best solution. Sometimes you won't remember.
shared_ptr 是一个 pointer wrapper,其实就是个模板类。它可以管理 raw pointer,最后自动释放内存。
template<typename T>
class shared_ptr;
ptr = new T;
s 表示 shared_ptr<T> s(ptr);
shared_ptr<T> x;
不会 new bookkeeping area(或者称为 control block),因此此函数是保证 noexcept 的。s.reset();
让 s 不再管理任何 ptr,并且,如果此时 use_count 为 1,(且 weak_count 为 1),则 Delete 其管理的 ptr。s.reset(new T(..));
相当于先调用上面的 .reset()
, 再换而管理另外一个 ptr2s.reset(ptr2);
来实现这种语义,其实现为 shared_ptr(ptr2).swap(*this);
s = shared_ptr<T>(ptr2);
这是创建了另外一个 shared_ptr,然后进行 move 和 swap。换言之,该 operator=()
的实现方式为 shared_ptr<T>(std::move(r)).swap(*this)
。其中 r 为 rhs(等号的右边 right-hand-side)s.reset(ptr); 或 s = shared_ptr<T>(ptr);
,最后在 s 析构时,ptr 会再次被 delete (this is an Undefined Bahaviour, may or may not corrupt the program)shared_ptr(__p).swap(*this);
,其中 __p = new T(..)
。退出 scope 时临时 rvalue shared_ptr (通过 swap 接管了原来的 ptr) 被析构,因此 ptr 被 deleteweak_ptr<T> w;
.lock()
: Returns a shared_ptr with the information preserved by the weak_ptr object if it is not expired.s.reset(ptr2)
,甚至 s.reset(ptr)
,都认为 object 过期。w 的 bka 共用原 s 的 bka,而原 s 的 bka 显示的 use_count 为 0,因此 lock 会失败,返回一个 default-constructed shared_ptr。_Sp_counted_base::_M_release
shared_ptr<T> sp;
up 表示 unique_ptr<T> up;
shared_ptr sp(...); sp = sp2;
此时 sp 先前所管理的 ptr 引用计数减一,转而管理 sp2unique_ptr up(...); up = up2;
编译违法,unique_ptr 只接受 move ctor (即 move constructor) 和 move = (即 move assignment operator)shared_ptr ps(move(up));
然后 up 失去对 ptr 的 ownership,成为一个 empty unique_ptr。shared_ptr sp(new T[3], [](T* p){ delete[] p; });
,否则 sp 析构时会默认用 delete p,而不是 delete[] p,导致问题shared_ptr sp(new T[3]);
,sp 析构时会正确调用 delete[] p;return std::shared_ptr<T>(this);
是错误的。这样每次会返回一个新的 shared_ptr,如果前面就存在一个指向该 object 的 shared_ptr,那么最后该 object 会被析构两次。return shared_from_this();
就可以返回一个和前面已经存在的 shared_ptr 管理同一个引用计数的 shared_ptrclass **T** : public std::enable_shared_from_this<**T**> { .. };
shared_ptr<T> x(new T(args...));
和 shared_ptr<T> x = make_shared(args...);
的区别:
shared_ptr<T> x(new T(args...));
会有两次 heap allocation:第一次是 new T,第二次是 shared_ptr 内部的 control block allocationshared_ptr<T> x = make_shared(args...);
中 new T 和 new control block 是在一个函数中完成的。引用 cppreference 的内容:
The object is constructed as if by the expression ::new (pv) T(std::forward<Args>(args)...), where pv is an internal void* pointer to storage suitable to hold an object of type T. The storage is typically larger than sizeof(T) in order to use one allocation for both the control block of the shared pointer and the T object.F(std::shared_ptr<Lhs>(new Lhs("foo")), std::shared_ptr<Rhs>(new Rhs("bar")));
Because C++ allows arbitrary order of evaluation of subexpressions, one possible ordering is:
new Lhs("foo"))
new Rhs("bar"))
std::shared_ptr<Lhs>
std::shared_ptr<Rhs>
Now, suppose we get an exception thrown at step 2 (e.g., out of memory exception, Rhs constructor threw some exception). We then lose memory allocated at step 1, since nothing will have had a chance to clean it up. The core of the problem here is that the raw pointer didn't get passed to the std::shared_ptr constructor immediately.
因此,一般如果已经提供了构造函数参数的,一般会优先选用 make_shared,make_unique,emplace 等操作执行。
std::atomic::fetch_add
with std::memory_order_relaxed
.shared_ptr<B> member
,而类B有 shared_ptr<A> member
。fun(shared_ptr<T>(new T), g);
有可能先 new T,再调用 g(),而一旦 g() 异常,则 new T 的内存泄露。